2010-05-12 5 views
0

Mon scénario particulier: - Le thread principal démarre un thread de travail. - thread principal doit se bloquer jusqu'à ce thread de travail est terminé (oui drôle) ou thread de travail lui-même informe thread principal pour aller surMonitor.Wait, Pulse - Lorsque le thread de travail doit se comporter comme un thread de travail sous conditions

Bon, ce que je l'ai fait en fil principal:

wokerThread.Start(lockObj); 
lock(lockObj) 
Monitor.Wait(lockObj); 

Quelque part en fil des travailleurs:

if(mainThreadShouldGoOn) 
lock(lockObj) 
    Monitor.Pulse(lockObj); 

en outre, à la fin du thread de travail:

lock(lockObj) 
Monitor.Pulse(lockObj); 

Jusqu'à présent, cela fonctionne parfaitement. Mais est-ce une bonne solution? Y en a t-il un meilleur?

EDIT:

Et si je le fais comme ça dans thread principal:

Monitor.Enter(lockObj); 
workerThread.Start(lockObj); 
Monitor.Wait(lockObj); 

Et travailleur ressemble à ceci:

void Worker(object lockObj) 
{ 
Monitor.Enter(lockObj); 
... 
... 
... 
if(mainThreadShouldGoOn) 
{ 
    Monitor.Pulse(lockObj); 
    Monitor.Exit(lockObj); 
} 
... 
... 
... 
if(!mainThreadShouldGoOn) 
{ 
    Monitor.Pulse(lockObj); 
    Monitor.Exit(lockObj); 
} 
} 

Répondre

0

Pourquoi voulez-vous commencer un thread de travail juste aller dormir juste après? Vous pouvez simplement faire le travail dans le fil principal!

Mais en supposant que vous avez une raison de le faire ...

Je pense que votre solution est ok sur le plan conceptuel, mais il pourrait y avoir un problème. Considérez le cas où un changement de contexte se produit après que le thread principal a démarré le worker mais avant qu'il n'acquière le verrou. Ensuite, le travailleur finirait rapidement son travail en une seule tranche de temps. Dans ce cas, Pulse est appelé avant Wait, donc plus tard le thread principal ne se réveillera jamais de l'attente. Voici une illustration:

class Program 
{ 
    static object theLock = new object(); 

    static void Main(string[] args) 
    { 
     Thread worker = new Thread(WorkerMain); 
     worker.Start(); 
     Thread.Sleep(1); 
     lock (theLock) 
     { 
      Monitor.Wait(theLock); 
     } 
     Console.WriteLine("Main done"); 
     Console.ReadLine(); 
    } 

    static void WorkerMain() 
    { 
     lock (theLock) 
     { 
      Monitor.Pulse(theLock); 
     } 
    } 
} 

La plupart du temps ce code va se bloquer. Vous pouvez résoudre le problème en déplaçant worker.Start à l'intérieur de la portée de la serrure.

Encore une chose à faire attention est de s'assurer que Pulse est appelé, même dans des cas exceptionnels. Utilisez essayer, c'est vrai, et tout ira bien. Ou envisager de séparer la tâche de l'ouvrier en deux parties: une qui doit toujours être effectuée, et le reste qui suit immédiatement après le contrôle if(mainThreadShouldGoOn) {...}. Ensuite, vous pouvez exécuter la première partie d'un thread de travail démarré par main, et la deuxième partie d'un autre thread de travail démarrée à partir du premier worker si nécessaire. Vous pouvez ensuite utiliser Thread.Join() pour attendre que le premier travail soit terminé. Cela rendrait la synchronisation plus simple et moins sujette aux erreurs en sacrifiant un peu de performance.

+0

Salut, merci pour votre réponse détaillée. Sur la base de vos suggestions, j'ai révisé ma solution.S'il vous plaît voir modifier dans mon message original. –

1

Ce code n'est pas correct, vous risquez de bloquer définitivement le thread principal si le travailleur termine trop tôt. L'objet de synchronisation approprié à utiliser ici est ManualResetEvent (ou auto, n'a pas d'importance). Appelez sa méthode Wait() dans le thread principal, sa méthode Set() dans le worker. Tant qu'il s'agit d'un Thread au lieu d'un thread de thread, l'utilisation de Thread.Join() fonctionnerait tout aussi bien.

+0

S'il vous plaît voir modifier dans mon message d'origine. –

Questions connexes