2010-07-26 18 views
5

Mi programa tiene una lista de 200k archivos. Tengo que importar cada uno a la base de datos. Me lleva mucho tiempo, así que comencé a investigar acerca de los multihilos como un medio para acelerar el proceso de importación. Finalmente llegué a una implementación, pero no estoy seguro de que realmente funcione.¿Cómo puedo verificar que mi código multiproceso se esté ejecutando realmente en varios hilos?

Después de usar Workaround for the WaitHandle.WaitAll 64 handle limit? como una muestra de mi código C# He ocurrió:

int threadCount = 0;  

for (int i = 0; i < this.Total; i++) 
{ 
     Finished = new ManualResetEvent(false); 
     threadCount = this.ThreadCount; 
     Interlocked.Increment(ref threadCount); 

     FileHandler fh = new FileHandler(finished, sorted[i], this.PicturesFeatures, this.Outcome, this.SiteIds, this.LastId, this.Order, this.ThreadCount); 
     Console.Write(i + " "); 
     ThreadPool.QueueUserWorkItem(new WaitCallback(HandleFile), fh); 
     Console.Write(i + " "); 
     Finished.WaitOne(); 
} 

Y HandleFile() sale como:

private void HandleFile(object s) 
    {   
     try 
     { 
      //code   
     } 
     finally 
     { 
      if (Interlocked.Decrement(ref threadCount) == 0) 
      { 
       Finished.Set(); 
      } 
     } 
    } 

he puesto esos Console.Write pensando que si un proceso es más largo terminaría más tarde que otro ("0 0 1 2 2 1 3 3 ..."), pero siempre está en orden ("0 0 1 1 2 2 3 3 4 4 ... ")

+0

Tengo un comentario: ¿está seguro de que su código gasta tiempo en computar cosas, en lugar de hacer operaciones de E/S de archivos y de bases de datos? De lo contrario, tenga en cuenta que hacer cosas en paralelo puede no acelerar demasiado el código (es decir, no es más rápido leer archivos en paralelo que leerlos uno por uno, excepto si los archivos están almacenados en discos duros diferentes). discos). –

+0

Sí, pero ¿qué pasa con la base de datos? Cada hilo abre una conexión diferente a la base de datos, por lo que debería acelerarse si utilizo el paralelismo. –

+0

no estoy tan seguro. Prueba y ve lo que sucede. En todos los casos, si el cuello de botella son operaciones de E/S de disco (localmente o en el nivel de la base de datos), la implementación del paralelismo ralentizará las cosas. –

Respuesta

4

Su salida es de esperar. Está escribiendo la salida en el hilo principal, la función QueueUserWorkItem no bloquea, registra su función HandleFile para ser ejecutada en un hilo separado. Por lo tanto, independientemente de cuánto tiempo tarden los elementos de trabajo, sus impresiones se realizarán en el orden esperado, ya que todas provienen del hilo principal.

Además, no obtiene el beneficio del paralelismo con este código porque está esperando cada elemento que envíe. Básicamente, estás diciendo que no enviaré mi próximo artículo de trabajo hasta que el último haya terminado. Esta es solo una forma más complicada de escribir un código serial normal. Para introducir el paralelismo, debe agregar varios elementos a la cola sin tener que esperar entre ellos.

1

En primer lugar, no se garantiza que los hilos generados a partir de una aplicación multiproceso terminen en un orden en particular. Es posible que haya comenzado primero un hilo, pero puede que no necesariamente termine primero.

Dicho esto, se puede utilizar Process Explorer: http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

Process Explorer le mostrará que se enrosca su programa es el desove.

2

Interrumpa la ejecución mientras se está ejecutando (ctrl + alt + break) y luego eche un vistazo a la ventana de subprocesos. (Depurar -> Windows -> Hilos).

+0

Ok. Tengo el hilo principal, 5 hilos de trabajo con , 1 hilo de trabajo llamado .NET SystemEvents y otro hilo de trabajo llamado vshost.RunParkingWindow. Lo comparé con algún programa de Hello World, parece que no hay muchos subprocesos en proceso. –

+0

¿Qué tan rápido se ejecutan sus hilos? Trata de poner un punto de quiebre en donde estarás bastante seguro de que tienes varios hilos ejecutándose simultáneamente. También puede hacer doble clic en los elementos en la ventana de subprocesos y saltará al punto de ejecución de ese subproceso. –

2

Tiene un par de problemas.

  • Los elementos de trabajo se serializarán eficazmente ya que está esperando a que se complete cada uno antes de comenzar el siguiente.
  • Las llamadas Console.WriteLine están en el hilo principal, por lo que es natural que informen i como incrementos en orden.

Aquí está el patrón canónico para hacer esto correctamente.

int count = TOTAL_ITERATIONS; 
var finished = new ManualResetEvent(false); 
for (int i = 0; i < TOTAL_ITERATIONS; i++) 
{ 
    int captured = i; // Use this for variable capturing in the anonymous method. 
    ThreadPool.QueueUserWorkItem(
    delegate(object state) 
    { 
     try 
     { 
     Console.WriteLine(captured.ToString()); 
     // Your task goes here. 
     // Refer to 'captured' instead of 'i' if you need the loop variable. 
     Console.WriteLine(captured.ToString()); 
     } 
     finally 
     { 
     if (Interlocked.Decrement(ref count) == 0) 
     { 
      finished.Set(); 
     } 
     } 
    }); 
} 
finished.WaitOne(); 

Editar: Para demostrar fácilmente que varios subprocesos se invocan uso el siguiente código.

public static void Main() 
{ 
    const int WORK_ITEMS = 100; 
    int count = WORK_ITEMS; 
    var finished = new ManualResetEvent(false); 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":Begin queuing..."); 
    for (int i = 0; i < WORK_ITEMS; i++) 
    { 
     int captured = i; // Use this for variable capturing in the anonymous method. 
     ThreadPool.QueueUserWorkItem(
      delegate(object state) 
      { 
       try 
       { 
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":" + captured.ToString()); 
        for (int j = 0; j < 100; j++) Thread.Sleep(1); 
        Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":" + captured.ToString()); 
       } 
       finally 
       { 
        if (Interlocked.Decrement(ref count) == 0) 
        { 
         finished.Set(); 
        } 
       } 
      }); 
    } 
    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString() + ":...end queueing"); 
    finished.WaitOne(); 
    Console.ReadLine(); 
} 
+0

He realizado los cambios que usted propone. Luego utilicé la ventana de subprocesos (como señaló Phil en la respuesta a continuación) y, como le dije a Phil, no parece tener mucho efecto. –

+0

@EduardoMello: Puede ser porque el 'ThreadPool' elige ejecutar los elementos de trabajo en serie solo en un hilo del grupo. Edité mi respuesta para incluir código que lo coacciona para asignar los elementos de trabajo a diferentes subprocesos y demuestra que de hecho lo está haciendo al escribir el id. De subproceso en la consola. –

1

La información que está produciendo proviene del mismo hilo (el que ejecuta el ciclo). Si desea ver evidencia de múltiples hilos, puede dar como resultado el nombre del hilo o algún otro valor de su función HandleFile.

Cuestiones relacionadas