Возможна ли память в ConcurrentBag?
Я читал новые параллельные коллекции, и особенно ConcurrentBag привлек мое внимание. Поскольку ConcurrentBag внутренне держит локальный набор в каждом отдельном streamе, используя его для отслеживания элементов, это означает, что когда сам stream выходит из области видимости, он все равно будет ссылаться в памяти на ConcurrentBag. Это, в свою очередь, означает как память, заявленную streamом, так и собственные ресурсы? (извините меня за то, что вы не знаете точной внутренней работы .NET-streamа)
Я могу предположить usecase, где у вас есть 1 глобальный ConcurrentBack для многопоточного веб-сервиса, где у вас много клиентов, добавляющих задачи. Эти задачи добавляются streamами в threadpool. Теперь threadpool – очень эффективный способ управления streamами, но он удаляет и создает streamи на основе объема работы. Таким образом, такая веб-служба может иногда сталкиваться с проблемами, поскольку основной пакет все еще ссылается на многие из них должны быть уничтожены нитями.
Я создал быстрое приложение для проверки этого поведения:
- Как асинхронно вызывать метод в Java
- Как дождаться завершения всех streamов, используя ExecutorService?
- Atomic UPDATE .. SELECT в Postgres
- Java Fork / Join vs ExecutorService - когда использовать какой?
- Как заставить BackgroundWorker возвращать объект
static ConcurrentBag bag = new ConcurrentBag(); static void FillBag() { for (int i = 0; i { FillBag(); PrintState(); }); // empty bag PrintState(); // first 100 items are added on main thread FillBag(); PrintState(); // second 100 items are added on remote thread remote.Start(); remote.Join(); // since the remote thread is gone out of scope, what happened to its local storage which is part of the bag? PrintState(); // now force a cleanup WeakReference weakRemoteReference = new WeakReference(remote); remote = null; GC.Collect(); GC.WaitForPendingFinalizers(); // Now check if the thread still exists if (weakRemoteReference.IsAlive) Console.WriteLine("Remote thread still exists"); PrintState(); Console.ReadLine();
И результат подтверждает мою историю:
Bag size is: 0 Bag size is: 100 Bag size is: 200 Bag size is: 200 Remote thread still exists Bag size is: 200
Можно ли ожидать такого поведения, допустил ли я ошибку в своем тесте или это можно считать недостатком дизайна?
- Инициализация двух streamов одним и тем же экземпляром исполняемого файла
- Более эффективный способ для цикла паузы
- Является ли это идиоматическим пулом рабочих streamов в Go?
- Есть ли способ для нескольких процессов совместно использовать прослушивающий сокет?
- Как разрешить декларацию с двойной проверкой блокировки в Java?
- Итерирует ли ConcurrentHashMap значение streamобезопасности?
- Зачем использовать ReentrantLock, если вы можете использовать синхронизированный (это)?
- Bash: ограничить количество одновременных заданий?
ConcurrentBag
действительно сохраняет в локальном хранилище streamов, и если вы откажетесь от streamов, это может привести к утечке памяти. Тем не менее, реализация может «украсть» элементы из списка одного streamа, чтобы передать другой stream. Вы можете увидеть это в действии, если вы напишете следующее:
ConcurrentBag MyBag = new ConcurrentBag (); void DoIt() { for (int i = 0; i < 10; ++i) { MyBag.Add(i); } ThreadPool.QueueUserWorkItem(EmptyBag); Console.Write("Press Enter:"); Console.ReadLine(); Console.WriteLine("{0} items in bag", MyBag.Count); } void EmptyBag(object state) { int take; while (MyBag.TryTake(out take)) { Console.WriteLine(take); } Console.WriteLine("Bag is empty"); }
Если вы запустите эту программу и дождитесь, пока сообщение «Мешок пуст», прежде чем вы нажмете Enter, вы увидите, что сумка действительно опустела.
Итак, до тех пор, пока в сумке будет одна нить, она в конечном итоге будет опустошена. Даже если все элементы были добавлены другими streamами.
Итак, да, есть возможная утечка памяти. На практике, однако, если несколько streamов обращаются к сумке, это, вероятно, не вызывает беспокойства.