¿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 _);
}
Daniel A. White
fuente
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 :-)

Adam Houldsworth
fuente
¿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.


fuente
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.

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

A partir de .NET Core 2.0 / .NET Standard 2.1 / .NET Framework 5.0, hay un Clear()método en ConcurrentBag<T>. Consulte: ConcurrentBag.Clear .

olabacker
fuente
-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.

un_pcnic
fuente