2011-03-21 18 views

Respuesta

25
T someItem; 
while (!myBag.IsEmpty) 
{ 
    myBag.TryTake(out someItem); 
} 
+21

Esto es definitivamente no atómico Por lo tanto, no debería agregarse como un método de extensión en ConcurrentBag, supongo, ya que todos los otros métodos se utilizan con la suposición de acceso atómico. – Anupam

+0

Además, ¿no puede TryTake fallar por otras razones además de estar vacío? – BrainSlugs83

+15

Esto es muy peligroso. Si otro proceso agrega elementos continuamente, esto podría seguir ocupado. – IvoTops

9

Puede eliminar objetos uno por uno en un bucle:

object result; 
while (bag.TryTake(out result)); 

Tenga en cuenta que la bolsa no está garantizada a estar vacío después de esta loop, porque los elementos se pueden agregar después de que el ciclo haya terminado y antes de la siguiente instrucción por un hilo diferente.

+0

Sí .. Estoy enfrentando el mismo error. Un hilo está tratando de limpiarlo en el mismo momento en que otro lo rellena ... por lo que no hay ninguna bolsa vacía ... – PawanS

+2

Esto es incorrecto por la misma razón por la que la respuesta aceptada es incorrecta. –

48

Actualización 10/03/2017: Como @Lou señala correctamente, la asignación es atómica. En este caso, la creación del ConcurrentBag no será atómica, pero poner esa referencia en la variable será ser atómica, por lo que no es estrictamente necesario bloquearla o Interlocked.Exchange.

leer un poco más lejos:

reference assignment is atomic so why is Interlocked.Exchange(ref Object, Object) needed?

Is a reference assignment threadsafe?


pudiera acceder siempre bloqueo a la propia bolsa y crear una nueva instancia de la misma. Los productos en la bolsa serán entonces elligible para GC si nada más se aferra a ellas:

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

O como señala Lukazoid a cabo:

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

Una forma sencilla de bin los contenidos, sin embargo, esto supone que cada vez que un elemento quiere acceder, también obtiene el bloqueo; esto podría ser costoso y podría anular el ajuste de rendimiento que se ha introducido en el ConcurrentBag.

si sabe que nada más va a tener acceso a la bolsa en este momento, ala-and-a-la oración y no bloquear :-)

+0

¿No sería una mejor solución simplemente tomar una copia de la referencia de la bolsa en cualquier lugar donde consuma la bolsa, entonces simplemente puede hacer 'bag = new' con impunidad? –

+1

@ChrisMarisic Sí, si puede evitar compartir datos, entonces no tiene estos problemas. La pregunta no tenía mucho contexto, sin embargo. –

+9

'Interlocked.Exchange' puede ser mejor que un bloqueo – Lukazoid

17

La respuesta seleccionada es una especie de, bueno, una solución, entonces estoy agregando mi propia solución.

Mi solución fue ver todas las colecciones disponibles en el espacio de nombres System.Collections.Concurrent para encontrar una en la que era trivial borrar todos los elementos de la colección. La clase ConcurrentStack tiene un método Clear() que elimina todos los elementos de la colección. De hecho, es la única colección en el espacio de nombres (actualmente) que lo hace. Sí, tienes que Push(T element) en lugar de Add(T element), pero francamente vale la pena el tiempo ahorrado.

+0

Sin embargo, hay muchas otras diferencias importantes entre las colecciones. Por ejemplo, si necesita determinar si un artículo dado está en la colección, es trivial y eficiente con una bolsa, pero no con una pila. – Servy

+0

@Servy: Sí, pero aún así. No, en realidad, comencé a escribir una lista pro/con, pero realmente depende de cuáles sean sus requisitos. Por ejemplo, las colecciones concurrentes son buenas para el acceso multiproceso, pero ninguna de ellas le permite indexar en la colección, algo que puede impedirle usarlas. La pregunta era específicamente sobre la eliminación de una bolsa concurrente (que es la razón por la que me encontré con ella), y la limpieza trivial de la colección con seguridad de hilo fue mi requisito. Mi respuesta fue cambiar las colecciones. – Will

+1

También uso la pila concurrente en lugar de la bolsa. Es extraño que la pila tenga Clear and Bag no. El objetivo principal de la bolsa es almacenar valores, comprobar existencia y eliminar todo o solo. Entonces la pila concurrente se convierte en algo similar a "una bolsa concurrente real un poco limitada". – Maxim

5

En el espíritu de las soluciones .. ConcurrentDictionary<T, bool> tiene un claro atómico, pero también le permite comprobar rápidamente si existe una clave. 'Rápidamente' es un término relativo, por supuesto, pero dependiendo de su uso, podría ser más rápido que enumerar una gran pila.

Cuestiones relacionadas