2011-02-16 14 views
22

Estoy utilizando el código de abajoLista de seguridad <T> hilo

var processed = new List<Guid>(); 
Parallel.ForEach(items, item => 
{ 
    processed.Add(SomeProcessingFunc(item)); 
}); 

es el hilo conductor el código de seguridad? ¿Hay alguna posibilidad de que la lista procesada se corrompa? ¿O debería usar un candado antes de agregar?

var processed = new List<Guid>(); 
Parallel.ForEach(items, item => 
{ 
    lock(items.SyncRoot) 
     processed.Add(SomeProcessingFunc(item)); 
}); 

gracias.

+0

¿Has mirado en MSDN? Aquí: http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx#c9721fa0-1cd9-4a21-818c-98d164c9fc14 –

+1

Ver http://stackoverflow.com/questions/4779165/parallel-foreach-loop -comportamiento raro. – mellamokb

+0

@Martinho: Sí. Leí que la lista no es segura para subprocesos. Pero no puedo entender que incluso si se agregan múltiples hilos a la lista, ¿cómo puede dañar la lista? – stackoverflowuser

Respuesta

23

No! No es seguro en absoluto, porque processed.Add no lo es. Puede hacer lo siguiente:

items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

Tenga en cuenta que Parallel.ForEach fue creado principalmente para imperativas operaciones para cada elemento de la secuencia. Lo que haces es un mapa: proyecta cada valor de secuencia. Para eso se creó Select. AsParallel lo escala entre subprocesos de la manera más eficiente.

Este código funciona correctamente:

var processed = new List<Guid>(); 
Parallel.ForEach(items, item => 
{ 
    lock(items.SyncRoot) 
     processed.Add(SomeProcessingFunc(item)); 
}); 

pero no tiene sentido en términos de múltiples hilos. lock ing en cada iteración obliga a la ejecución totalmente secuencial, el grupo de subprocesos estará esperando un subproceso único.

+0

completa: por qué: consulte http://msdn.microsoft.com/en-us/library/6sh2ey19.aspx cerca del final, tiene un tema sobre Seguridad de subprocesos – rene

+0

@rene: tack '# c9721fa0-1cd9-4a21-818c -98d164c9fc14' hasta el final de esa dirección y apunta directamente a la sección correspondiente;) –

+0

Gracias por la respuesta. ¿Sería mejor utilizar ConcurrentBag como se menciona aquí http://stackoverflow.com/questions/4779165/parallel-foreach-loop-odd-behavior – stackoverflowuser

4

citar Jon Skeet antes de que llegue aquí:

Como parte de las extensiones Parellel en .Net 4, hay varias nuevas colecciones en un nuevo espacio de nombres System.Collections.Concurrent . Estos están diseñados para ser seguros frente a las operaciones concurrentes de varios hilos, con relativamente poco bloqueo.

Incluyen IProducerConsumerCollection<T>, BlockingCollection<T>, ConcurrentBag<T>, ConcurrentQueue<T>, ConcurrentStack<T>, and ConcurrentDictionary<TKey, TValue> entre otros.

+0

De acuerdo. Ver mi respuesta, que usa un tipo del espacio de nombres System.Collections.Concurrent. – mellamokb

0

lectura es seguro para subprocesos, pero no es así. Necesita una configuración de bloqueo de lector/escritor ya que la adición puede hacer que la matriz interna cambie el tamaño, lo que estropearía una lectura simultánea.

Si puede garantizar que la matriz no cambiará de tamaño al agregar, puede estar seguro de agregar mientras lee, pero no me cite al respecto.

Pero en realidad, una lista es solo una interfaz para una matriz.

0

Como alternativa a la answer de Andrey:

items.AsParallel().Select(item => SomeProcessingFunc(item)).ToList(); 

También podría escribir

items.AsParallel().ForAll(item => SomeProcessingFunc(item)); 

Esto hace que la consulta que está detrás de él aún más eficiente porque no se requiere de combinación, MSDN. Asegúrese de que la función SomeProcessingFunc es segura para subprocesos. Y creo, pero no lo he probado, que todavía necesita un bloqueo si la lista se puede modificar en otro hilo (agregando o eliminando) elementos.

1

Uso ConcurrentBag de tipo Algo

var bag = new ConcurrentBag<List<Something>>; 
var items = GetAllItemsINeed(); 
Parallel.For(items,i =>       
    { 
     bag.Add(i.DoSomethingInEachI()); 
    });