9

El siguiente código explica mi pregunta. Sé que la lista no es segura para subprocesos. ¿Pero cuál es la razón "real" subyacente de esto?hilos múltiples que agregan elementos a una lista. ¿Por qué hay siempre menos elementos en la lista de lo esperado?

class Program 
{ 
    static void Main(string[] args) 
    { 
     List<string> strCol = new List<string>(); 

     for (int i = 0; i < 10; i++) 
     { 
      int id = i; 
      Task.Factory.StartNew(() => 
      { 
       AddElements(strCol); 
      }).ContinueWith((t) => { WriteCount(strCol, id.ToString()); }); 
     } 

     Console.ReadLine(); 
    } 

    private static void WriteCount(List<string> strCol, string id) 
    { 
     Console.WriteLine(string.Format("Task {0} is done. Count: {1}. Thread ID: {2}", id, strCol.Count, Thread.CurrentThread.ManagedThreadId)); 
    } 

    private static void AddElements(List<string> strCol) 
    { 
     for (int i = 0; i < 20000; i++) 
     { 
      strCol.Add(i.ToString()); 
     } 
    } 
} 
+1

parece que algunos de los hilos se escriben entre sí. La lista interna debe estar usando un contador para incrementar a la siguiente posición, y como no es seguro para subprocesos, sobrescribe los valores –

Respuesta

15

Me saltearé la respuesta obvia "La lista no es segura para subprocesos", esto ya lo sabes.

Los elementos de la lista se guardan en una matriz interna. Hay al menos dos etapas (desde el punto de vista lógico) al agregar un elemento a una lista. Primero, List obtiene un índice que indica dónde colocar el nuevo elemento. Pone un nuevo elemento en una matriz usando este índice. Luego incrementa el índice y esta es la etapa dos. Si ocurre un segundo (o tercer, cuarto, ...) hilo para agregar un nuevo elemento al mismo tiempo, es posible que haya dos (3, 4, ...) nuevos elementos puestos en la misma ubicación del arreglo antes del índice se incrementa por el primer hilo Los artículos se sobrescriben y se pierden.

Las operaciones internas de agregar nuevo elemento e incrementar el índice deben realizarse siempre de una vez para que la lista sea segura para la ejecución de subprocesos. Eso es lo que se llama sección crítica. Esto se puede lograr con bloqueos.

Espero que esto explique un poco.

13

Esto se debe a List<T> no es seguro para subprocesos.

Debe utilizar una colección segura de subprocesos para esto, como una de las colecciones en System.Collections.Concurrent. De lo contrario, deberá sincronizar todos los accesos al List<T> (es decir, poner cada llamada a Add dentro de un bloqueo), lo que frustrará el propósito de llamar esto usando múltiples hilos por completo, ya que no está haciendo ningún otro trabajo en esta situación .

+0

como se indica en la pregunta. Sé que la lista no es segura para subprocesos. ¿Pero cuál es la razón "real" subyacente de esto? – CuiPengFei

+2

@CuiPengFei la razón "real" es probable que haya operaciones (copias de datos, tamaños de matriz, etc.) que se realizan sin bloqueo, bajo la suposición de que el desarrollador no está interactuando con la 'Lista' de múltiples hilos a la vez. La sincronización adicional necesaria para asegurar la seguridad de la cadena 'List' tendría un impacto significativo en el rendimiento, por lo que los diseñadores de BCL eligieron omitirlo y documentar la falta de seguridad de subprocesos, de modo que si necesita acceso seguro a subprocesos usted mismo puede compilarlo. Lo mismo que en 'ArrayList' de Java y muchas otras clases' List'. –

+0

@CuiPengFei: Porque, al no ser seguro para subprocesos, el acto de agregar a 'List ' no es completamente atómico. Un hilo comienza a agregar un elemento, otro hilo lo cortocircuita agregando su propio elemento. – David

Cuestiones relacionadas