Як видалити всі елементи з ConcurrentBag?


Відповіді:


37

Хоча це може бути не зовсім зрозумілим через потенційні умови перегонів, цього достатньо:

while (!myBag.IsEmpty) 
{
   myBag.TryTake(out T _);
}

23
Це точно не атомно. Тому не слід додавати його як метод розширення на ConcurrentBag, я думаю, оскільки всі інші методи використовуються з припущенням про атомний доступ.
Анупам

Крім того, чи не може TryTake вийти з ладу з інших причин, крім того, що він порожній?
BrainSlugs83,

21
Це дуже небезпечно. Якщо інший процес додає елементи безперервно, це може залишатися зайнятим.
IvoTops

4
Відповідь від @Adam Houldsworth + мій коментар є кращою.
Кріс Марісіч

1
якщо це небезпечне рішення, чому досі прийнято відповідь?
Barış Akkurt

65

Оновлення 10.03.2017: Як правильно зазначає @Lou, призначення є атомним. У цьому випадку створення ConcurrentBagволі не буде атомним, але введення цього посилання у змінну буде атомним - тому блокування або Interlocked.Exchangeнавколо нього не є строго необхідним.

Деякі подальші читання:

присвоєння посилання є атомним, то чому потрібен Interlocked.Exchange (ref Object, Object)?

Чи безпечне присвоєння посилань?


Ви завжди можете заблокувати доступ до самої сумки та створити її нову копію. Тоді предмети, що знаходяться в мішку, зможуть отримати GC, якщо на них більше нічого не тримається:

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

Або як зазначає Луказоїд:

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

Проте це простий спосіб прив'язати вміст, проте, якщо елемент хоче отримати доступ, він також отримує блокування - це може коштувати дорого і може заперечити налаштування продуктивності, яке ввійшло в ConcurrentBagсебе.

Якщо ви знаєте, що в цей час ніщо інше не отримає доступу до сумки, моліться і не блокуйте :-)


Чи не кращим рішенням буде просто взяти копію посилання на сумку в будь-якому місці, де ви споживаєте сумку, тоді ви можете просто bag = newбезкарно робити ?
Кріс Марісіч

1
@ChrisMarisic Так, якщо ви взагалі можете уникнути обміну даними, то ви позбавлені цих проблем. Однак питання не мало особливого контексту.
Адам Голдсворт

10
Interlocked.Exchangeможе бути краще замку
Луказоїд,

5
Як це Interlocked.Exchangeпрацює, якщо під час обміну до сумки додається ще одна нитка? Це Addзамикається під час Exchange?
Брендон

2
Присвоєння атомні в .NET, як блокування, так і Interlocked.Exchangeзайві тут (і не забезпечують безпеки потоків).
Лу

24

Вибрана відповідь є свого роду, ну, обхідним шляхом, тому я додаю своє власне обхідне рішення.

Моє рішення полягало в тому, щоб переглянути всі доступні колекції в просторі імен System.Collections.Concurrent, щоб знайти таку, де було б тривіально очистити всі елементи з колекції.

Клас ConcurrentStack має метод Clear (), який видаляє всі елементи з колекції. Насправді це єдина колекція у просторі імен (на даний момент), яка це робить. Так, вам доведеться Push(T element)замість цього Add(T element), але, чесно кажучи, це коштує витраченого часу.


1
Однак є багато інших важливих відмінностей між колекціями. Наприклад, якщо вам потрібно визначити, чи є певний предмет у колекції, це і тривіально, і ефективно з сумкою, але не зі стеком.
Серві

@Servy: Так, але все-таки. Ні, насправді, я почав писати список за / проти, але це насправді залежить від ваших вимог. Наприклад, одночасні колекції гарні для багатопотокового доступу, але жодна з них не дозволяє індексувати колекцію, що може заважати вам використовувати їх. Питання полягало конкретно в очищенні одночасної сумки (саме тому я і натрапив на неї), і тривіальне очищення колекції за допомогою безпеки потоків було моєю вимогою. Моєю відповіддю було змінити колекції.

2
Я також використовую паралельний стек замість сумки. Дивно, що стек має Clear, а Bag немає. Основне призначення пакета - зберігати цінності, перевіряти наявність та видаляти все або одиничне. Отже, паралельний стек стає чимось схожим на "трохи обмежений реальний паралельний пакет".
Максим

@Servy, як можна ефективно визначити, чи міститься даний елемент у ConcurrentBag? Я не бачу жодної власної властивості чи методу для цього. ContainsНе злічити. Це метод розширення для загальних IEnumerables, і він є чим завгодно, але не ефективним.
Theodor Zoulias

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.