Je n'avais jamais vraiment utilisé de threading auparavant en C # où je devais avoir deux threads, ainsi que le thread d'interface utilisateur principal. Fondamentalement, j'ai ce qui suit.
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();
}
Donc, essentiellement, ma question est de savoir comment faire en sorte qu'un fil en attende un autre pour se terminer. Quelle est la meilleure façon de procéder?
Je peux voir 5 options disponibles:
Comme avec la réponse de Mitch. Mais cela bloquera votre thread d'interface utilisateur, mais vous obtenez un délai d'expiration intégré pour vous.
WaitHandle
ManualResetEvent
est un WaitHandle
comme suggéré par Jrista.
Une chose à noter est que si vous voulez attendre plusieurs threads, WaitHandle.WaitAll()
ne fonctionnera pas par défaut, car il nécessite un thread MTA. Vous pouvez contourner ce problème en marquant votre méthode Main()
avec MTAThread
. Toutefois, cela bloque la pompe à messages et n'est pas recommandé d'après ce que j'ai lu.
Voir cette page de Jon Skeet à propos des événements et du multi-threading, il est possible qu'un événement ne devienne pas abonné entre le if
et le EventName(this,EventArgs.Empty)
- c'est déjà arrivé auparavant.
(si tout va bien, je n'ai pas essayé de les compiler)
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);
}
}
}
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<T> or Func<T>
public void Run(object state)
{
// Do a task
Action completeAction = (Action)state;
completeAction.Invoke();
}
}
}
Si vous utilisez la méthode _count, cela pourrait être une idée (par sécurité) de l’incrémenter en utilisant
Interlocked.Increment(ref _count)
Je serais intéressé de connaître la différence entre l'utilisation de délégués et d'événements pour la notification de thread, la seule différence que je connaisse sont que les événements sont appelés de manière synchrone.
La réponse à cette question décrit très clairement vos options avec cette méthode.
La manière événementielle/déléguée de faire signifiera que votre méthode de gestionnaire d'événement est sur thread1/thread2 et non dans l'interface utilisateur principale. thread , il vous faudra donc revenir au début des méthodes HandleThreadDone:
// Delegate example
if (InvokeRequired)
{
Invoke(new Action(HandleThreadDone));
return;
}
Ajouter
t1.Join(); // Wait until thread t1 finishes
après que vous l’ayez démarré, mais cela ne fera pas grand chose car c’est le même résultat que de courir sur le thread principal!
Je peux fortement recommander la lecture du livre électronique gratuit de Joe Albahari Threading in C # , si vous voulez comprendre le threading dans .NET.
Les deux réponses précédentes sont excellentes et fonctionneront pour des scénarios simples. Il existe cependant d'autres moyens de synchroniser les threads. Ce qui suit fonctionnera également:
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 est l'un des différents WaitHandle proposés par le framework .NET. Ils peuvent fournir des fonctionnalités de synchronisation de threads bien plus riches que les outils simples mais très courants tels que lock ()/Monitor, Thread.Join, etc. Ils peuvent également être utilisés pour synchroniser plus de deux threads, permettant ainsi des scénarios complexes tels qu'un thread "maître". qui coordonne plusieurs threads 'enfants', plusieurs processus simultanés dépendants de la synchronisation de plusieurs étapes, etc.
Si vous utilisez depuis .NET 4, cet exemple peut vous aider:
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
}
}
à partir de: https://stackoverflow.com/a/4190969/1676736
Vous voulez la méthode Thread.Join()
, ou l'une de ses surcharges .
J'aurais votre thread principal passer une méthode de rappel à votre premier thread, et quand c'est fait, il invoquera la méthode de rappel sur le thread principal, qui peut lancer le second thread. Cela empêche votre fil principal de rester en attente pendant qu'il attend une jointure ou une attente. Passer des méthodes en tant que délégués est une chose utile à apprendre avec C # de toute façon.
Publier pour peut-être aider d'autres personnes a passé pas mal de temps à chercher une solution semblable à celle que j'ai proposée. J'ai donc adopté une approche légèrement différente. Il y a une option de compteur ci-dessus, je viens de l'appliquer un peu différemment. Je filais de nombreux threads et incrémentais un compteur et décrémentais un compteur lorsqu'un thread commençait et s'arrêtait. Ensuite, dans la méthode principale, je voulais faire une pause et attendre que les threads soient terminés.
while (threadCounter > 0)
{
Thread.Sleep(500); //Make it pause for half second so that we don’t spin the cpu out of control.
}
Documenté sur mon blog. http://www.adamthings.com/post/2012/07/11/ensure-threads-have-finished-before-method-continues-in-c/
Lorsque je souhaite que l'interface utilisateur puisse mettre à jour son affichage en attendant la fin d'une tâche, j'utilise une boucle while qui teste IsAlive sur le thread:
Thread t = new Thread(() => someMethod(parameters));
t.Start();
while (t.IsAlive)
{
Thread.Sleep(500);
Application.DoEvents();
}