2010-06-12 7 views
74

Con el nuevo ConcurrentBag<T> en .NET 4, ¿cómo se elimina un determinado objeto específico cuando solo TryTake() y TryPeek() están disponibles?¿Cómo eliminar un único objeto específico de un ConcurrentBag <>?

Estoy pensando en usar TryTake() y luego simplemente añadiendo el objeto resultante de nuevo en la lista si No desea eliminarlo, pero me siento como puede ser que falte algo. ¿Es esta la manera correcta?

Respuesta

63

La respuesta corta: no se puede hacer de una manera fácil.

ConcurrentBag mantiene una cola local de subprocesos para cada subproceso y solo mira las colas de otros subprocesos una vez que su propia cola se vuelve vacía. Si quitas un artículo y lo vuelves a colocar, el próximo artículo que elimines puede ser el mismo artículo otra vez. No hay garantía de que eliminar elementos repetidamente y volver a colocarlos le permitirá iterar sobre todos los elementos.

dos alternativas para usted:

  • eliminar todos los elementos y recordarlos, hasta que encuentre el que desea quitar y, a continuación, poner los otros de nuevo más tarde. Tenga en cuenta que si dos hilos intentan hacer esto simultáneamente, tendrá problemas.
  • Utilice una estructura de datos más adecuada como ConcurrentDictionary.
+2

SynchronizedCollection también podría ser un sustituto adecuado. –

+0

@ILIABROUDNO - ¡debes poner eso como respuesta! Eso es MUCHO mejor que un kludgey ConcurrentDictionary cuando no necesitas un Dictionary – Denis

3

Como mencionas, TryTake() es la única opción. Este es también el ejemplo en MSDN. El reflector tampoco muestra otros métodos internos de interés ocultos.

13

No puede. Es una bolsa, no está ordenada. Cuando lo vuelvas a poner, simplemente te quedarás atrapado en un ciclo sin fin.

Quiere un conjunto. Puede emular uno con ConcurrentDictionary. O un HashSet que te proteges con un candado.

+6

Amplía. ¿Qué usarías como clave en el ConcurrentDictionary subyacente? –

+2

Bueno, supongo que la clave sería el tipo del objeto que intentas almacenar, y luego el valor sería una colección de algún tipo. Eso sería "emular" un 'HashSet' como él describe. –

-12

¿qué tal:

bag.Where(x => x == item).Take(1); 

Funciona, no estoy seguro de qué tan eficientemente ...

+0

Esto no elimina nada de la bolsa. El artículo que está recuperando permanece dentro de la bolsa. – Keith

+3

debería ser "bag = new ConcurrentBag (bag.Where (x => x! = Item))" – atikot

+3

@atikot, esa línea me hizo reír – parek

-4
public static ConcurrentBag<String> RemoveItemFromConcurrentBag(ConcurrentBag<String> Array, String Item) 
{ 
    var Temp=new ConcurrentBag<String>(); 
    Parallel.ForEach(Array, Line => 
    { 
     if (Line != Item) Temp.Add(Line); 
    }); 
    return Temp; 
} 
1

El ConcurrentBag es grande para manejar una lista donde se pueden agregar elementos y enumerar a partir muchos subprocesos, luego eventualmente descartarlo como su nombre lo sugiere :)

As Mark Byers told, puede volver a compilar un nuevo ConcurrentBag que no contenga el elemento que desea eliminar, bu t tiene que proteger esto contra hits de múltiples hilos usando un bloqueo. Se trata de una sola línea:

myBag = new ConcurrentBag<Entry>(myBag.Except(new[] { removedEntry })); 

Esto funciona, y coincide con el espíritu con el que el ConcurrentBag ha sido diseñado.

+0

Creo que esta respuesta es engañosa. Para que quede claro, esto NO proporciona seguridad de subprocesos en la operación de eliminación deseada. Y poner un candado alrededor de eso frustra el propósito de usar una colección simultánea. –

+0

Estoy de acuerdo. Bueno, para aclarar un poco, el ConcurrentBag está diseñado para ser llenado, enumerado y desechado con todo su contenido cuando esté listo. Cualquier intento, incluido el mío, de eliminar un elemento provocará un ataque sucio. Al menos traté de dar una respuesta, aunque lo mejor es utilizar una mejor clase de colección concurrente, como el ConcurrentDictionary. – Larry

2

Mark tiene razón en que el ConcurrentDictionary funcionará de la manera que usted desea. Si aún desea usar un ConcurrentBag, lo siguiente, que no es eficiente, lo llevará hasta allí.

var stringToMatch = "test"; 
var temp = new List<string>(); 
var x = new ConcurrentBag<string>(); 
for (int i = 0; i < 10; i++) 
{ 
    x.Add(string.Format("adding{0}", i)); 
} 
string y; 
while (!x.IsEmpty) 
{ 
    x.TryTake(out y); 
    if(string.Equals(y, stringToMatch, StringComparison.CurrentCultureIgnoreCase)) 
    { 
     break; 
    } 
    temp.Add(y); 
} 
foreach (var item in temp) 
{ 
    x.Add(item); 
} 
1
public static void Remove<T>(this ConcurrentBag<T> bag, T item) 
{ 
    while (bag.Count > 0) 
    { 
     T result; 
     bag.TryTake(out result); 

     if (result.Equals(item)) 
     { 
      break; 
     } 

     bag.Add(result); 
    } 

} 
2

Esta es mi clase de extensión que estoy usando en mis proyectos. Se puede eliminar un solo elemento de ConcurrentBag y también puede eliminar la lista de los artículos en la bolsa

public static class ConcurrentBag 
{ 
    static Object locker = new object(); 

    public static void Clear<T>(this ConcurrentBag<T> bag) 
    { 
     bag = new ConcurrentBag<T>(); 
    } 


    public static void Remove<T>(this ConcurrentBag<T> bag, List<T> itemlist) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 

       Parallel.ForEach(itemlist, currentitem => { 
        removelist.Remove(currentitem); 
       }); 

       bag = new ConcurrentBag<T>(); 


       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 

    public static void Remove<T>(this ConcurrentBag<T> bag, T removeitem) 
    { 
     try 
     { 
      lock (locker) 
      { 
       List<T> removelist = bag.ToList(); 
       removelist.Remove(removeitem);     

       bag = new ConcurrentBag<T>(); 

       Parallel.ForEach(removelist, currentitem => 
       { 
        bag.Add(currentitem); 
       }); 
      } 

     } 
     catch (Exception ex) 
     { 
      Debug.WriteLine(ex.Message); 
     } 
    } 
} 
Cuestiones relacionadas