Come aspettare che il thread finisca con .NET?

Non ho mai usato il threading in C # in cui ho bisogno di avere due thread, oltre al thread dell’interfaccia utente principale. Fondamentalmente, ho il seguente.

public void StartTheActions() { //Starting thread 1.... Thread t1 = new Thread(new ThreadStart(action1)); t1.Start(); // Now, I want for the main thread (which is calling `StartTheActions` method) // to wait for `t1` to finish. I've created an event in `action1` for this. // The I wish `t2` to start... Thread t2 = new Thread(new ThreadStart(action2)); t2.Start(); } 

Quindi, in sostanza, la mia domanda è come avere un thread in attesa che ne finisca un altro. Qual è il modo migliore per farlo?

Riesco a vedere 5 opzioni disponibili:

1. Thread.Join

Come con la risposta di Mitch. Ma questo bloccherà il thread dell’interfaccia utente, tuttavia otterrai un timeout integrato per te.


2. Utilizzare un WaitHandle

ManualResetEvent è un WaitHandle come suggerito da jrista.

Una cosa da notare è che se vuoi aspettare più thread, WaitHandle.WaitAll() non funzionerà di default, in quanto ha bisogno di un thread MTA. Puoi aggirare questo problema contrassegnando il tuo metodo Main() con MTAThread – tuttavia questo blocca il tuo messaggio pump e non è raccomandato da ciò che ho letto.


3. Fai fuoco su un evento

Vedi questa pagina di Jon Skeet su eventi e multi-threading, è ansible che un evento possa diventare non sottoscritto tra if e EventName(this,EventArgs.Empty) – mi è successo prima.

(Si spera che questi siano compilati, non ho provato)

 public class Form1 : Form { int _count; void ButtonClick(object sender, EventArgs e) { ThreadWorker worker = new ThreadWorker(); worker.ThreadDone += HandleThreadDone; Thread thread1 = new Thread(worker.Run); thread1.Start(); _count = 1; } void HandleThreadDone(object sender, EventArgs e) { // You should get the idea this is just an example if (_count == 1) { ThreadWorker worker = new ThreadWorker(); worker.ThreadDone += HandleThreadDone; Thread thread2 = new Thread(worker.Run); thread2.Start(); _count++; } } class ThreadWorker { public event EventHandler ThreadDone; public void Run() { // Do a task if (ThreadDone != null) ThreadDone(this, EventArgs.Empty); } } } 

4. Utilizzare un delegato

 public class Form1 : Form { int _count; void ButtonClick(object sender, EventArgs e) { ThreadWorker worker = new ThreadWorker(); Thread thread1 = new Thread(worker.Run); thread1.Start(HandleThreadDone); _count = 1; } void HandleThreadDone() { // As before - just a simple example if (_count == 1) { ThreadWorker worker = new ThreadWorker(); Thread thread2 = new Thread(worker.Run); thread2.Start(HandleThreadDone); _count++; } } class ThreadWorker { // Switch to your favourite Action or Func public void Run(object state) { // Do a task Action completeAction = (Action)state; completeAction.Invoke(); } } } 

Se usi il metodo _count, potrebbe essere un’idea (per sicurezza) incrementarlo usando

Interlocked.Increment(ref _count)

Sarei interessato a conoscere la differenza tra l’utilizzo di delegati e eventi per la notifica di thread, l’unica differenza che so sono gli eventi sono chiamati in modo sincrono.


5. In modo asincrono invece

La risposta a questa domanda ha una descrizione molto chiara delle opzioni con questo metodo.


Debind / Eventi nella discussione sbagliata

Il modo in cui gli eventi / delegati fanno le cose significherà che il metodo del gestore di eventi è su thread1 / thread2 e non sul thread principale dell’interfaccia utente , quindi dovrai tornare indietro nella parte superiore dei metodi HandleThreadDone:

 // Delegate example if (InvokeRequired) { Invoke(new Action(HandleThreadDone)); return; } 

Inserisci

 t1.Join(); // Wait until thread t1 finishes 

dopo averlo avviato, ma ciò non porterà a risultati molto importanti in quanto è essenzialmente lo stesso risultato del thread principale!

Consiglio vivamente di leggere l’e-book gratuito di Threading in C # di Joe Albahari, se si vuole comprendere il threading in .NET.

Le due risposte precedenti sono ottime e funzioneranno per scenari semplici. Esistono altri modi per sincronizzare i thread, comunque. Quanto segue funzionerà anche:

 public void StartTheActions() { ManualResetEvent syncEvent = new ManualResetEvent(false); Thread t1 = new Thread( () => { // Do some work... syncEvent.Set(); } ); t1.Start(); Thread t2 = new Thread( () => { syncEvent.WaitOne(); // Do some work... } ); t2.Start(); } 

ManualResetEvent è una delle varie WaitHandle che il framework .NET ha da offrire. Possono fornire funzionalità di sincronizzazione dei thread più ricche rispetto ai semplici ma molto comuni strumenti come lock () / Monitor, Thread.Join, ecc. Possono anche essere utilizzati per sincronizzare più di due thread, consentendo scenari complessi come un thread ‘master’ che coordina più thread “figlio”, più processi simultanei che dipendono da più fasi l’uno dall’altro per essere sincronizzati, ecc.

Se si utilizza da .NET 4 questo esempio può aiutarti:

 class Program { static void Main(string[] args) { Task task1 = Task.Factory.StartNew(() => doStuff()); Task task2 = Task.Factory.StartNew(() => doStuff()); Task task3 = Task.Factory.StartNew(() => doStuff()); Task.WaitAll(task1, task2, task3); Console.WriteLine("All threads complete"); } static void doStuff() { //do stuff here } } 

da: https://stackoverflow.com/a/4190969/1676736

Vuoi il metodo Thread.Join() o uno dei suoi overload .

Avrei il tuo thread principale passare un metodo di callback al tuo primo thread e, una volta terminato, invocherà il metodo di callback sul mainthread, che può avviare il secondo thread. Ciò mantiene il filo principale sospeso mentre attende un Join o Waithandle. Passare i metodi come delegati è comunque una cosa utile da imparare con C #.

Pubblicare per aiutare forse altri, ha trascorso un po ‘di tempo alla ricerca di una soluzione come quella che mi è venuta in mente. Quindi ho adottato un approccio leggermente diverso. C’è un’opzione contatore sopra, l’ho appena applicata un po ‘diversamente. Stavo girando numerosi thread e ho incrementato un contatore e ho decrementato un contatore mentre un thread iniziava e si fermava. Poi nel metodo principale volevo fare una pausa e aspettare che i thread si completassero.

 while (threadCounter > 0) { Thread.Sleep(500); //Make it pause for half second so that we don't spin the cpu out of control. } 

Documentato sul mio blog. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

Prova questo:

 List myThreads = new List(); foreach (Thread curThread in myThreads) { curThread.Start(); } foreach (Thread curThread in myThreads) { curThread.Join(); }