web-dev-qa-db-fra.com

Comment "tuer" complètement le travailleur en arrière-plan?

J'écris une application Windows qui exécute une séquence d'actions numériques IO à plusieurs reprises.

Cette séquence d'actions démarre lorsque l'utilisateur clique sur un bouton "START" et elle est effectuée par un travailleur en arrière-plan dans backgroundWorker1_DoWork ().

Cependant, il y a des occasions où j'obtiens le message d'erreur "Ce backgroundworker est actuellement occupé .......".

Je pense à implémenter ce qui suit dans le code, en utilisant une boucle while pour "tuer" le travailleur en arrière-plan avant de commencer une autre séquence d'action:

if (backgroundWorker1.IsBusy == true)
{

    backgroundWorker1.CancelAsync();
    while (backgroundWorker1.IsBusy == true)
    {
        backgroundWorker1.CancelAsync();
    }

    backgroundWorker1.Dispose();

}

backgroundWorker1.RunWorkerAsync();

Je pense que ma principale préoccupation est la suivante: le backgroundWorker1 sera-t-il "tué" à terme? Si c'est le cas, cela prendra-t-il beaucoup de temps pour le terminer?

Ce codage me mettra-t-il dans une boucle infinie?

54
Ken Hung

Vous pouvez utiliser quelque chose comme ceci (pour plus d'informations sur l'abandon des threads gérés et sur ThreadAbortException, voir " Plumbing the Depths of the ThreadAbortException Using Rotor " by Chris Sells):

public class AbortableBackgroundWorker : BackgroundWorker
{

    private Thread workerThread;

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        workerThread = Thread.CurrentThread;
        try
        {
            base.OnDoWork(e);
        }
        catch (ThreadAbortException)
        {
            e.Cancel = true; //We must set Cancel property to true!
            Thread.ResetAbort(); //Prevents ThreadAbortException propagation
        }
    }


    public void Abort()
    {
        if (workerThread != null)
        {
            workerThread.Abort();
            workerThread = null;
        }
    }
}

Usage:

backgroundWorker1 = new AbortableBackgroundWorker();
//...
backgroundWorker1.RunWorkerAsync();

if (backgroundWorker1.IsBusy == true)
{
    backgroundWorker1.Abort();
    backgroundWorker1.Dispose();
}
75
Sergey Teplyakov

Je suis d'avis que les threads devraient être responsables de leurs ressources propres autant que possible, y compris leur propre durée de vie.

C'est généralement une mauvaise idée de tuer les threads en dehors de leur portée. Les applications conçues pour transmettre un message au thread pour se fermer lui-même ont tendance à avoir beaucoup moins de problèmes liés au comportement multithread.

Un thread doit surveiller ledit message, qui peut être aussi simple qu'un booléen défini par un autre thread et lu par ce thread de surveillance, en temps opportun et se fermer proprement dès qu'il le peut.

Cela signifie que s'il doit rechercher le message:

  • dans sa boucle principale, le cas échéant.
  • périodiquement dans toutes les boucles de longue durée.

Le thread qui l'arrête avec le message devrait attendre (mais n'arrêtez pas l'interface graphique, bien sûr).

Notez qu'il existe d'autres possibilités pour les environnements filetés avec des capacités spécifiques telles que le cas où les threads peuvent se marquer annulables à volonté, pour permettre une destruction externe plus sûre.

Mais il est généralement plus facile de simplement architecturer votre application pour laisser un maître de thread de son propre destin.

18
paxdiablo

J'en ai mis un ensemble qui (je pense) fait le travail. S'il vous plaît laissez-moi savoir si je suis hors tension. Voici un exemple simple de son fonctionnement.

var backgroundWorker = new BackgroundWorker(){WorkerSupportsCancellation = true};

backgroundWorker.DoWork += (sender, args) =>
         {                 
                 var thisWorker = sender as BackgroundWorker;
                 var _child = new Thread(() =>
                                               {
                                                   //..Do Some Code

                                               });
                 _child .Start();
                 while (_child.IsAlive)
                 {
                     if (thisWorker.CancellationPending)
                     {
                         _child.Abort();
                         args.Cancel = true;
                     }
                     Thread.SpinWait(1);
                 }                 
         };

 backgroundWorker.RunWorkerAsync(parameter);
 //..Do Something...
backgroundWorker.CancelAsync();

Étant donné que le travailleur en arrière-plan fait partie du pool de threads, nous ne voulons pas l'abandonner. Mais nous pouvons exécuter un thread en interne sur lequel nous pouvons autoriser un abandon. Le backgroundWorker s'exécute ensuite jusqu'à ce que le thread enfant soit terminé ou que nous lui signalions de tuer le processus. Le thread de travail d'arrière-plan peut ensuite revenir dans le pool de lecture. En général, je vais envelopper cela dans une classe d'assistance et passer par la méthode déléguée que je veux que le thread d'arrière-plan s'exécute en tant que paramètre et l'exécuter dans le thread enfant.

S'il vous plaît, quelqu'un m'a fait savoir si je me cognais la tête contre un mur mais cela semble bien fonctionner .. Mais c'est le problème avec les threads n'est-ce pas .. les résultats variables que vous pouvez obtenir lorsque vous l'exécutez à des moments différents.

0
Rob

J'avais le même problème, je ne sais pas si cela va aider, mais je suppose que votre travailleur en arrière-plan a une boucle à l'intérieur ou il se fermerait. Ce que vous devez faire, c'est mettre votre boucle à l'intérieur.

Mettez à l'intérieur de votre travailleur de fond:

while (backgroundworker1.CancellationPending == false)
{
    //Put your code in here
}

Pour tuer ce backgroundworker, vous pouvez mettre votre bouton:

BackgroundWorker1.CancelAsync()

J'espère que ça aide.

0
echrom