Jak usunąć wszystkie elementy z ConcurrentBag?

Odpowiedzi:

37

Chociaż może to nie być całkowicie jasne ze względu na potencjalny stan wyścigu, jest to wystarczające:

while (!myBag.IsEmpty) 
{
   myBag.TryTake(out T _);
}
Daniel A. White
źródło
23
To zdecydowanie nie jest atomowe. Tak więc nie powinno być dodawane jako metoda rozszerzająca do ConcurrentBag, jak sądzę, ponieważ wszystkie inne metody są używane przy założeniu dostępu atomowego.
Anupam
Ponadto, czy tryb TryTake nie może się nie powieść z innych powodów niż to, że jest pusty?
BrainSlugs83,
21
Jest to bardzo niebezpieczne. Jeśli inny proces stale dodaje elementy, może to być zajęte.
IvoTops
4
Odpowiedź od @Adam Houldsworth + mój komentarz jest lepsza.
Chris Marisic,
1
jeśli jest to niebezpieczne rozwiązanie, dlaczego nadal przyjmuje się odpowiedź?
Barış Akkurt
65

Aktualizacja 10/03/2017: Jak słusznie wskazuje @Lou, przypisanie jest niepodzielne. W tym przypadku utworzenie ConcurrentBagwill nie będzie atomowe, ale umieszczenie tego odniesienia w zmiennej będzie atomowe - czyli zablokowanie lubInterlocked.Exchange otaczanie go nie jest ściśle wymagane.

Dalsze czytanie:

przypisanie odniesienia jest niepodzielne, więc dlaczego potrzebne jest Interlocked.Exchange (ref Object, Object)?

Czy przypisanie odwołania jest bezpieczne dla wątków?


Zawsze możesz zablokować dostęp do samej torby i utworzyć jej nową instancję. Przedmioty w torbie będą wtedy kwalifikowały się do GC, jeśli nic innego ich nie trzyma:

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

Lub, jak wskazuje Lukazoid:

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

Łatwy sposób na przechowywanie zawartości, jednak zakłada to, że gdy element chce uzyskać dostęp, również zostaje zablokowany - może to być kosztowne i może zniweczyć dostrojenie wydajności, które zaszło w ConcurrentBag samego siebie.

Jeśli wiesz, że w tej chwili nic innego nie będzie miało dostępu do torby, złóż ją i nie zamykaj :-)

Adam Houldsworth
źródło
Czy nie byłoby lepszym rozwiązaniem, gdybyś zabrał kopię numeru referencyjnego torby w dowolnym miejscu, w którym ją konsumujesz, a potem możesz to zrobić bag = newbezkarnie?
Chris Marisic,
1
@ChrisMarisic Tak, jeśli w ogóle możesz uniknąć udostępniania danych, nie masz takich problemów. Pytanie nie miało jednak zbyt dużego kontekstu.
Adam Houldsworth
10
Interlocked.Exchangemoże być lepszy niż zamek
Lukazoid
5
Jak Interlocked.Exchangedziała, jeśli w momencie wymiany do torby dodaje się kolejną nitkę? Czy to Addsię blokuje w trakcie Exchange?
Brandon
2
Przydziały są atomowe w .NET, zarówno blokada, jak i Interlocked.Exchangesą tutaj nadmiarowe (i nie zapewniają bezpieczeństwa wątków).
Lou
24

Wybrana odpowiedź jest pewnego rodzaju obejściem, więc dodaję własne obejście.

Moim rozwiązaniem było przejrzenie wszystkich dostępnych kolekcji w przestrzeni nazw System.Collections.Concurrent, aby znaleźć taką, w której wyczyszczenie wszystkich elementów z kolekcji było trywialne.

ConcurrentStack klasa ma clear () metodę, która usuwa wszystkie elementy z tej kolekcji. W rzeczywistości jest to jedyna kolekcja w przestrzeni nazw (obecnie), która to robi. Tak, musisz Push(T element)zamiast tego Add(T element), ale szczerze mówiąc, warto zaoszczędzić czas.


źródło
1
Pomiędzy kolekcjami jest jednak wiele innych ważnych różnic. Na przykład, jeśli chcesz określić, czy dana pozycja znajduje się w kolekcji, jest to zarówno trywialne, jak i wydajne w przypadku torby, ale nie w przypadku stosu.
Servy
@Servy: Tak, ale nadal. Nie, właściwie zacząłem pisać listę za / przeciw, ale to naprawdę zależy od twoich wymagań. Na przykład kolekcje współbieżne są dobre w przypadku dostępu wielowątkowego, ale żadna z nich nie pozwala na indeksowanie do kolekcji, co może uniemożliwić korzystanie z nich. Pytanie dotyczyło w szczególności opróżnienia równoczesnego worka (i dlatego go spotkałem), a moim wymaganiem było trywialne czyszczenie kolekcji z zabezpieczeniem nici. Moja odpowiedź była taka, żeby zmienić kolekcje.
2
Używam również równoczesnego stosu zamiast worka. To dziwne, że stos ma Clear, a Bag nie. Głównym celem torby jest przechowywanie wartości, sprawdzanie istnienia i usuwanie wszystkich lub pojedynczych. Tak więc stos współbieżny staje się czymś w rodzaju „nieco ograniczonego rzeczywistego worka współbieżnego”.
Maxim
@Servy, jak możesz skutecznie określić, czy dany element jest zawarty w ConcurrentBag? Nie widzę żadnej natywnej właściwości ani metody, aby to zrobić. To Containssię nie liczy. Jest to metoda rozszerzająca dla ogólnych IEnumerables i nie jest skuteczna.
Theodor Zoulias
9

W duchu obejść .. ConcurrentDictionary<T, bool>posiada atomic Clear, ale pozwala też szybko sprawdzić czy klucz istnieje. „Szybko” to oczywiście termin względny, ale w zależności od zastosowania może być szybszy niż wyliczanie dużego stosu.

OlduwanSteve
źródło
Niezłe! Należy to uznać za typ kontenera wybrany do takich przypadków. ConcurrentStack podobnie.
Opóźnienie
4

Od wersji .NET Core 2.0 / .NET Standard 2.1 / .NET Framework 5.0 dostępna jest Clear()metoda ConcurrentBag<T>. Zobacz: ConcurrentBag.Clear .

olabacker
źródło
-2
int cnt = _queue.Count;
for (; cnt > 0; cnt--)
{
     _queue.TryDequeue(out img);
}

Nie wpada w nieskończoną pętlę i oczyszcza zawartość obecnego czasu.

a_pcnic
źródło