Как удалить все элементы из ConcurrentBag?

Ответы:

37

Хотя это может быть не полностью очищено из-за потенциального состояния гонки, этого достаточно:

while (!myBag.IsEmpty) 
{
   myBag.TryTake(out T _);
}
Дэниел А. Уайт
источник
23
Это определенно не атомарно. Поэтому я полагаю, что не следует добавлять в ConcurrentBag в качестве метода расширения, поскольку все другие методы используются с предположением атомарного доступа.
Anupam
Кроме того, не может ли TryTake выйти из строя по другим причинам, кроме того, что он пуст?
BrainSlugs83
21
Это очень опасно. Если другой процесс постоянно добавляет элементы, он может оставаться занятым.
IvoTops 06
4
Ответ от @Adam Houldsworth + мой комментарий лучше.
Крис Марисич,
1
если это опасное решение, почему он все еще принят?
Barış Akkurt
65

Обновление 10/03/2017: как правильно указывает @Lou, назначение является атомарным. В этом случае создание объекта ConcurrentBagне будет атомарным, но помещение этой ссылки в переменную будет атомарным, поэтому блокировка или блокировка Interlocked.Exchangeне требуется.

Дальнейшее чтение:

присвоение ссылки является атомарным, так зачем нужен Interlocked.Exchange (ref Object, Object)?

Является ли задание ссылки потокобезопасным?


Вы всегда можете заблокировать доступ к самой сумке и создать ее новый экземпляр. После этого предметы в сумке будут доступны для сборки мусора, если за них больше ничего не держится:

lock (something)
{
    bag = new ConcurrentBag();
}

Или, как указывает Луказоид:

var newBag = new ConcurrentBag();
Interlocked.Exchange<ConcurrentBag>(ref bag, newBag);

Тем не менее, простой способ поместить содержимое в корзину. При этом предполагается, что всякий раз, когда элементу требуется доступ, он также получает блокировку - это может быть дорого и может свести на нет настройку производительности, которая вошла в ConcurrentBagсаму себя.

Если вы знаете, что в это время к сумке никто не получит доступ, молитесь и молитесь и не закрывайте ее :-)

Адам Хулдсворт
источник
Разве не было бы лучшим решением просто взять копию ссылки на сумку в любом месте, где вы ее потребляете, тогда вы можете просто делать это bag = newбезнаказанно?
Крис Марисик,
1
@ChrisMarisic Да, если вы вообще можете избежать обмена данными, тогда у вас нет этих проблем. Однако у вопроса не было особого контекста.
Адам Хулдсворт,
10
Interlocked.Exchangeможет быть лучше замка
Lukazoid
5
Как Interlocked.Exchangeработает, если во время обмена в корзину добавляется другой поток? Это Addзаблокировано во время Exchange?
Брэндон,
2
В .NET присвоения являются атомарными, здесь и блокировка, и Interlocked.Exchangeизбыточность (и не обеспечивают безопасность потоков).
Лу
24

Выбранный ответ - это своего рода обходной путь, поэтому я добавляю свой собственный обходной путь.

Мое решение заключалось в том, чтобы просмотреть все доступные коллекции в пространстве имен System.Collections.Concurrent, чтобы найти ту, в которой было бы тривиально удалить все элементы из коллекции.

У класса ConcurrentStack есть метод Clear (), который удаляет все элементы из коллекции. Фактически, это единственная коллекция в пространстве имен (в настоящее время), которая это делает. Да, Push(T element)вместо этого придется Add(T element), но, честно говоря, это стоит сэкономленного времени.


источник
1
Однако между коллекциями есть много других важных различий. Например, если вам нужно определить, находится ли данный элемент в коллекции, это тривиально и эффективно с Bag, но не с Stack.
Servy
@Servy: Да, но все же. Нет, вообще-то я начал писать список за / против, но это действительно зависит от ваших требований. Например, параллельные коллекции хороши для многопоточного доступа, но ни одна из них не позволяет вам индексировать в коллекции, что может помешать вам их использовать. Вопрос был конкретно об очистке параллельного пакета (поэтому я столкнулся с ним), и тривиальная очистка коллекции с безопасностью потоков была моим требованием. Мой ответ - сменить коллекцию.
2
Я также использую параллельный стек вместо мешка. Странно, что в стеке нет Clear, а Bag нет. Основное назначение сумки - хранить ценности, проверять наличие и удалять все или отдельные. Таким образом, параллельный стек становится чем-то вроде «немного ограниченного реального параллельного пакета».
Maxim
@Servy, как вы можете эффективно определить, содержится ли данный элемент в a ConcurrentBag? Я не вижу никаких собственных свойств или методов для этого. Это Containsне считается. Это метод расширения для общих IEnumerables, и он совсем не эффективен.
Теодор Зулиас
9

В духе обходных путей .. ConcurrentDictionary<T, bool>имеет атомарный Clear, но также позволяет быстро проверить, существует ли ключ. «Быстро», конечно, относительный термин, но в зависимости от вашего использования он может быть быстрее, чем перечисление большого стека.

ОлдуванСтив
источник
Хороший! Это следует рассматривать как тип контейнера, выбранный для таких случаев. ConcurrentStack аналогично.
Задержка
4

Начиная с .NET Core 2.0 / .NET Standard 2.1 / .NET Framework 5.0, есть Clear()метод ConcurrentBag<T>. См. ConcurrentBag.Clear .

олабакер
источник
-2
int cnt = _queue.Count;
for (; cnt > 0; cnt--)
{
     _queue.TryDequeue(out img);
}

Он не попадает в бесконечный цикл, а очищает содержимое настоящего времени.

a_pcnic
источник