Como esperar que o thread termine com o .NET?

Eu nunca usei realmente threading antes em c # onde eu preciso ter dois segmentos, bem como o segmento principal da interface do usuário. Basicamente, tenho o seguinte.

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

Então, essencialmente, minha pergunta é como ter um thread esperando por outro para terminar. Qual é a melhor maneira de fazer isso?

Eu posso ver 5 opções disponíveis:

1. Thread.Join

Como com a resposta de Mitch. Mas isso bloqueará seu thread de interface do usuário, no entanto, você terá um Tempo limite embutido para você.


2. Use um WaitHandle

ManualResetEvent é um WaitHandle como jrista sugerido.

Uma coisa a notar é que se você quiser esperar por vários segmentos, o WaitHandle.WaitAll() não funcionará por padrão, já que ele precisa de um thread MTA. Você pode contornar isso marcando seu método Main() com o MTAThread – no entanto, isso bloqueia sua bomba de mensagens e não é recomendado pelo que eu li.


3. Atire um evento

Veja esta página por Jon Skeet sobre events e multi-threading, é possível que um evento possa se tornar não-inscrito entre o if e o EventName(this,EventArgs.Empty) – já aconteceu comigo antes.

(Espero que estes compilar, eu não tentei)

 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. Use um delegado

 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 você usar o método _count, pode ser uma ideia (para ser seguro) incrementá-lo usando

Interlocked.Increment(ref _count)

Eu estaria interessado em saber a diferença entre o uso de delegates e events para notificação de thread, a única diferença que eu sei são events são chamados de forma síncrona.


5. Faça de forma assíncrona

A resposta a esta pergunta tem uma descrição muito clara de suas opções com este método.


Delegar / events no segmento errado

O evento / delegado maneira de fazer as coisas vai significar o seu método de manipulador de events está em thread1 / thread2 não o segmento principal da interface do usuário , então você precisará alternar de volta à direita no topo dos methods HandleThreadDone:

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

Adicionar

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

depois de iniciá-lo, mas isso não vai conseguir muito, pois é essencialmente o mesmo resultado que a execução no thread principal!

Eu recomendo muito ler o livro eletrônico de Joe Albahari, ” Threading in C # “, se você quiser entender o threading do .NET.

As duas respostas anteriores são ótimas e funcionarão para cenários simples. Existem outras maneiras de sincronizar os threads, no entanto. O seguinte também funcionará:

 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 é um dos vários WaitHandle que o .NET framework tem a oferecer. Eles podem fornecer resources de synchronization de threads muito mais ricos do que as ferramentas simples, mas muito comuns como lock () / Monitor, Thread.Join, etc. Eles também podem ser usados ​​para sincronizar mais de dois threads, permitindo cenários complexos como um thread ‘master’ que coordena vários encadeamentos ‘filhos’, vários processos simultâneos que dependem de vários estágios uns dos outros para serem sincronizados, etc.

Se usando a partir do .NET 4 este exemplo pode ajudá-lo:

 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 } } 

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

Você quer o método Thread.Join() ou uma de suas sobrecargas .

Eu teria seu segmento principal passar um método de retorno de chamada para seu primeiro segmento, e quando estiver pronto, ele invocará o método de retorno de chamada no mainthread, que pode iniciar o segundo segmento. Isso mantém seu thread principal pendurado enquanto aguarda por um Join ou Waithandle. Passando methods como delegates é uma coisa útil para aprender com o C # de qualquer maneira.

Postando para talvez ajudar alguns outros, gastei um pouco de tempo procurando uma solução como a que eu criei. Então eu peguei uma abordagem diferente. Existe uma opção de contador acima, eu apenas apliquei um pouco diferente. Eu estava girando vários tópicos e incrementado um contador e diminuí um contador como um segmento iniciado e interrompido. Então, no método principal, eu estava querendo pausar e esperar que os threads fossem concluídos.

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

Documentado no meu blog. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/

Tente isto:

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