2010-07-26 11 views
5

Il mio programma ha una lista di file 200k. Devo importare ciascuno nel database. Mi ci vuole molto tempo, quindi ho iniziato a fare ricerche sui multithread come mezzo per accelerare il processo di importazione. Ho finalmente ottenuto un'implementazione ma non sono sicuro che funzioni effettivamente.Come posso verificare che il mio codice multithreading sia effettivamente in esecuzione su più thread?

Dopo aver usato Workaround for the WaitHandle.WaitAll 64 handle limit? come campione per il mio codice C# che ho si avvicinò con:

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(); 
} 

E HandleFile() va come:

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

ho messo quei Console.Write pensando che se un processo è più lungo finirebbe più tardi di qualche altro ("0 0 1 2 2 1 3 3 ..."), ma è sempre in ordine ("0 0 1 1 2 2 3 3 4 4 ... ")

+0

Ho un'osservazione: sei sicuro che il tuo codice impieghi tempo per il calcolo, piuttosto che eseguire operazioni di I/O e database dei file? Altrimenti, sii consapevole del fatto che fare cose in parallelo potrebbe non velocizzare troppo il tuo codice (ad esempio non è più veloce leggere i file in parallelo che leggerli uno per uno, eccetto se i file sono memorizzati su hard differenti dischi). –

+0

Sì, ma che dire del database? Ogni thread apre una connessione diversa al database, quindi dovrebbe accelerare se utilizzo il parallelismo. –

+0

non così sicuro. Prova e vedi cosa succede. In tutti i casi, se il collo di bottiglia è rappresentato da operazioni di I/O su disco (localmente oa livello di database), l'implementazione del parallelismo rallenterà le cose. –

risposta

4

L'output è previsto. Stai scrivendo l'output nel thread principale, la funzione QueueUserWorkItem non si blocca, registra la tua funzione HandleFile da eseguire in un thread separato. Quindi, indipendentemente da quanto tempo durano gli elementi di lavoro, le stampe avverranno nell'ordine previsto, poiché sono tutte provenienti dal thread principale.

Inoltre, non ottieni il vantaggio del parallelismo con questo codice perché stai aspettando dopo ogni articolo inviato. In sostanza, stai dicendo che non invierò il mio prossimo oggetto di lavoro fino a quando non sarà terminato l'ultimo. Questo è solo un modo più complicato di scrivere un normale codice serializzato. Per introdurre il parallelismo, è necessario aggiungere più elementi alla coda senza attendere tra una sessione e l'altra.

1

Prima di tutto, i thread generati da un'applicazione multithread non sono garantiti per terminare in un ordine particolare. Potresti aver avviato un thread per primo, ma potrebbe non necessariamente terminare per primo.

Detto questo, è possibile utilizzare Process Explorer: http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx

Process Explorer vi mostrerà che infila il vostro programma è la deposizione delle uova.

2

Interrompere l'esecuzione mentre è in esecuzione (ctrl + alt + interruzione) e quindi dare un'occhiata alla finestra dei thread. (Debug -> Windows -> Thread).

+0

Ok. Ho il thread principale, 5 thread di lavoro con , 1 thread di lavoro chiamato .NET SystemEvents e un altro thread di lavoro chiamato vshost.RunParkingWindow. Ho fatto un paragone con un programma Hello World, non sembra che sia in atto un multithreading. –

+0

Quanto velocemente eseguono i tuoi thread? Prova a inserire un punto di interruzione in cui sarai quasi sicuro di avere più thread in esecuzione contemporaneamente. Inoltre è possibile fare doppio clic sugli elementi nella finestra dei thread e salterà al punto di esecuzione per quel thread. –

2

Hai un paio di problemi.

  • Gli elementi di lavoro verranno serializzati in modo efficace poiché si sta aspettando che ciascuno completi prima di iniziare il successivo.
  • Le chiamate Console.WriteLine si trovano sul thread principale, quindi è naturale per loro segnalare i come in ordine crescente.

Ecco il modello canonico per farlo correttamente.

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(); 

Edit: per dimostrare facilmente che più thread vengono richiamati utilizzare il seguente codice.

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

Ho apportato le modifiche come proposto. Poi ho usato la finestra del thread (come indicato da Phil sulla risposta in basso) e, come ho detto a Phil, non sembra avere molto effetto. –

+0

@ EduardoMello: Può essere perché il 'ThreadPool' sta scegliendo di eseguire gli elementi di lavoro in serie su un solo thread dal pool. Ho modificato la mia risposta per includere il codice che la costringe ad assegnare gli elementi di lavoro a thread diversi e dimostra che in effetti lo fa scrivendo l'ID del thread alla console. –

1

Le informazioni che stai trasmettendo provengono tutte dallo stesso thread (quello che esegue il loop). Se si desidera vedere la prova di più thread, è possibile generare il nome del thread o qualche altro valore dalla funzione HandleFile.

Problemi correlati