Как удалить все элементы из 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 аналогично.
— Задержка


-2
int cnt = _queue.Count;
for (; cnt > 0; cnt--)
{
     _queue.TryDequeue(out img);
}

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.