Interrompre un fil en sumil

Est-il possible d’interrompre un fil en sumil? Si j’ai un code similaire à celui-ci.

while(true){ if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1){ DoWork(); _lastExecuteTime = DateTime.Now(); continue; } Thread.Sleep(10000) //Sleep 10 seconds if(somethingIndicatingQuit){ break; } } 

Je veux exécuter DoWork () toutes les heures. Donc, j’aimerais dormir un peu plus longtemps que 10 secondes. Dites check toutes les 10 minutes environ. Cependant, si je mets mon sumil à 10 minutes et que je veux éliminer cette tâche en arrière-plan, je dois attendre que le sumil reprenne.

Mon code actuel utilise un Threading.ManualResetEvent pour arrêter le travail en arrière-plan, mais mon problème concerne le code ThreadSleep. Je peux poster plus de code si nécessaire.

OK, je vais append un code un peu plus complet ici, car je pense qu’il va répondre à certaines des questions.

 private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false); private readonly ManualResetEvent _pauseEvent = new ManualResetEvent(true); private Thread _backGroundWorkerThread; //This starts our work public void Start() { _backGroundWorkerThread = new Thread(ExecuteWorker) {IsBackground = true, Name = WorkerName + "_Thread"}; _shutdownEvent.Reset(); _backGroundWorkerThread.Start(); } internal void Stop() { //Signal the shutdown event _shutdownEvent.Set(); //Make sure to resume any paused threads _pauseEvent.Set(); //Wait for the thread to exit _backGroundWorkerThread.Join(); } private void ExecuteWorker() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); //This kills our process if (_shutdownEvent.WaitOne(0)) { break; } if (!_worker.IsReadyToExecute) { //sleep 5 seconds before checking again. If we go any longer we keep our service from shutting down when it needs to. Thread.Sleep(5000); continue; } DoWork(); } } 

Mon problème est ici,

 _backGroundWorkerThread.Join(); 

Cela attend le Thread.Sleep dans la ExecuteWorker () qui s’exécute dans mon thread d’arrière-plan.

Au lieu d’utiliser Thread.Sleep utilisez ManualResetEvent.WaitOne .

 while (true) { if(DateTime.Now.Subtract(_lastExecuteTime).TotalHours > 1) { DoWork(); _lastExecuteTime = DateTime.Now(); continue; } if (terminate.WaitOne(10000)) { break; } } 

terminate est un ManualResetEvent 1 que vous pouvez Set pour demander la fin de la boucle.

Mettre à jour:

Je viens de remarquer que vous avez dit que vous utilisez déjà ManualResetEvent pour terminer le travail de fond (je suppose que cela se trouve dans DoWork ). Y a-t-il une raison pour laquelle vous ne pouvez pas utiliser le même MRE? Si cela n’est pas possible, il ne devrait certainement pas être question d’en utiliser un autre.

Mise à jour 2:

Ouais, alors au lieu de Thread.Sleep(5000) dans ExecuteWorker faites _shutdownEvent.WaitOne(5000) . Cela ressemblerait à ce qui suit.

 private void ExecuteWorker() { while (true) { _pauseEvent.WaitOne(Timeout.Infinite); //This kills our process if (_shutdownEvent.WaitOne(0)) { break; } if (!_worker.IsReadyToExecute) { //sleep 5 seconds before checking again. If we go any longer we keep our service from shutting down when it needs to. _shutdownEvent.WaitOne(5000); continue; } DoWork(); } } 

1 Il existe également une classe ManualResetEventSlim dans .NET 4.0.

Au lieu d’utiliser Thread.Sleep , vous pouvez utiliser Monitor.Wait avec un délai d’expiration. Vous pouvez ensuite utiliser Monitor.Pulse partir d’un autre thread pour le Monitor.Pulse .

N’oubliez pas que vous devez verrouiller l’écran avant d’appeler Wait ou Pulse :

 // In the background thread lock (monitor) { // If we've already been told to quit, we don't want to sleep! if (somethingIndicatingQuit) { break; } Monitor.Wait(monitor, TimeSpan.FromSeconds(10)); if (somethingIndicatingQuit) { break; } } // To wake it up... lock (monitor) { somethingIndicatingQuit = true; Monitor.Pulse(monitor); } 

Vous pouvez utiliser Thead.Interrupt pour réactiver un thread en veille. Cela provoquera une ThreadInteruptedException sur le thread bloqué. Ce n’est donc pas l’approche la plus élégante ni la plus efficace.

Vous devez également être fatigué car interrompre un fil de cette manière est dangereux. Il ne permet pas de contrôler le point auquel le thread est abandonné et ne doit donc pas être utilisé sans un examen minutieux des conséquences. Comme mentionné dans d’autres réponses, il est de loin préférable d’utiliser des techniques basées sur le signal pour contrôler le moment où le thread se termine de manière contrôlée.

Ne pouvez-vous pas simplement envelopper votre partie Thread.Sleep dans une boucle de façon à ce qu’elle boucle 60 fois, puis effectue l’autre vérification? Bien sûr, tout ce que cela fait est un échange simple si (quelque chose d’ IndicatingQuit ) par rapport à DateTIme si cocher, mais en supposant que votre code réel fasse quelque chose de plus cher, avoir une seconde boucle pourrait offrir des économies de temps CPU minimes.

Si vous voulez DoWork () toutes les heures, pourquoi ne pas calculer le nombre de ms requirejs jusqu’à l’heure suivante et attendre ce moment? Pour annuler la mise en veille, vous pouvez utiliser une classe de synchronisation synchronisable, comme le moniteur, comme le suggère @Jon Skeet, ou simplement définir un indicateur dans le fil qui indique à celui-ci de se fermer après la mise en veille au lieu de DoWork () et de l’oublier. – soit le thread se tuera quand le temps sera écoulé, soit votre système d’exploitation le tuera à la fermeture de l’application, selon la première éventualité.

Rgds, Martin

pourquoi ne pas utiliser un BlockingCollection? Je le vois comme une approche beaucoup plus élégante que la réponse acceptée. Je ne pense pas non plus qu’il y ait beaucoup de frais généraux par rapport au moniteur.

Voici comment faire:

initialiser une BlockingCollection en tant que membre:

 BlockingCollection _sleeper = new BlockingCollection(); 

au lieu de

 Thread.Sleep(10000) 

fais ça

 int dummy; _sleeper.TryTake(out dummy, 10000); 

pour le réveiller, vous pouvez utiliser ce code

 _sleeper.Add(0);