¿Cómo eliminar todos los elementos de ConcurrentBag?


Respuestas:


37

Aunque puede que no esté completamente claro debido a una posible condición de carrera, esto es suficiente:

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

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

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

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

4
La respuesta de @Adam Houldsworth + mi comentario es mejor.
Chris Marisic

1
si esta es una solución peligrosa, ¿por qué todavía se acepta la respuesta?
Barış Akkurt

65

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

Algunas lecturas adicionales:

La asignación de referencia es atómica, entonces, ¿por qué se necesita Interlocked.Exchange (ref Object, Object)?

¿Es una tarea de referencia segura para subprocesos?


Siempre puede bloquear el acceso a la bolsa y crear una nueva instancia de la misma. Los artículos en la bolsa serán elegibles para GC si nada más los está reteniendo:

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

O como señala Lukazoid:

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

Sin embargo, una manera fácil de agrupar el contenido, asume que siempre que un elemento desea acceder, también obtiene el bloqueo; esto podría ser costoso y podría anular el ajuste de rendimiento que se ha realizado en ConcurrentBagsí mismo.

Si sabe que nada más accederá a la bolsa en este momento, alójela y reza y no la cierre :-)


¿No sería una mejor solución simplemente tomar una copia de la referencia de la bolsa en cualquier lugar donde consumas la bolsa, entonces puedes hacerlo bag = newcon impunidad?
Chris Marisic

1
@ChrisMarisic Sí, si puede evitar compartir datos, entonces está libre de estos problemas. Sin embargo, la pregunta no tenía mucho contexto.
Adam Houldsworth

10
Interlocked.Exchangepuede ser mejor que un candado
Lukazoid

5
¿Cómo Interlocked.Exchangefunciona si se agrega otro hilo a la bolsa en el momento del intercambio? ¿ AddEso se bloquea durante el Exchange?
Brandon

2
Las asignaciones son atómicas en .NET, tanto el bloqueo como Interlocked.Exchangeson redundantes aquí (y no proporcionan seguridad para subprocesos).
Lou

24

La respuesta seleccionada es una especie de, bueno, una solución alternativa, así que estoy agregando mi propia solución.

Mi solución fue mirar todas las colecciones disponibles en el espacio de nombres System.Collections.Concurrent para encontrar una en la que fuera 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í, tiene que Push(T element)hacerlo en lugar de hacerlo Add(T element), pero, francamente, vale la pena el tiempo ahorrado.


1
Sin embargo, hay muchas otras diferencias importantes entre las colecciones. Por ejemplo, si necesita determinar si un elemento determinado está en la colección, es trivial y eficiente con una Bolsa, pero no con una Pila.
Servicio

@Servy: Sí, pero aún así. No, en realidad, comencé a escribir una lista de pros y contras, pero realmente depende de cuáles sean tus 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 limpieza de una bolsa concurrente (por eso me encontré con ella), y mi requisito era la limpieza trivial de la colección con seguridad para subprocesos. Mi respuesta fue cambiar de colección.

2
También uso pila concurrente en lugar de bolsa. Es extraño que el stack tenga Clear y Bag no. El propósito principal de la bolsa es almacenar valores, verificar la existencia y eliminar todo o solo. Así que la pila concurrente se convierte en algo similar a "una bolsa concurrente real un poco limitada".
Maxim

@Servy, ¿cómo se puede determinar de manera eficiente si un elemento determinado está contenido en un ConcurrentBag? No veo ninguna propiedad o método nativo para hacer eso. El Containsno cuenta. Es un método de extensión para IEnumerables genéricos , y es todo menos eficiente.
Theodor Zoulias

9

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


¡Buena esa! Este debe considerarse como el tipo de contenedor elegido para tales casos. ConcurrentStack de manera similar.
Latencia


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

No cae en un bucle infinito y borra el contenido del tiempo presente.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.