77 votes

Comment arrêter correctement BackgroundWorker


J'ai un formulaire avec 2 zones de liste modifiables sur elle. Et je veux remplir combobox2.DataSource basé sur combobox1.Text et combobox2.Text (je suppose que l'utilisateur a terminé l'entrée en combobox1 et est dans le milieu de la saisie en combobox2). J'ai donc un gestionnaire d'événements pour l' combobox2 comme ceci:

private void combobox2_TextChanged(object sender, EventArgs e)
{
    if (cmbDataSourceExtractor.IsBusy)
       cmbDataSourceExtractor.CancelAsync();

    var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
       V2 = combobox2.Text};
    cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
}

Aussi loin que la construction de la source de données est le processus qui prend du temps (il crée une requête de base de données et l'exécute), j'ai décidé qu'il est préférable de l'effectuer dans un autre processus à l'aide de BackgroundWorker. Donc, il y a un scénario lorsque cmbDataSourceExtractor n'a pas terminé son travail et que l'utilisateur tape un symbole de plus. Dans ce cas, j'obtiens une exception sur cette ligne
cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues ); sur que BackgroundWorker est occupé et ne peut pas effectuer plusieurs actions en même temps.
Comment se débarrasser de cette exception?
Merci à l'avance!

102voto

HackedByChinese Points 18294

CancelAsync ne fait pas les abandonner votre fil ou quelque chose comme ça. Il envoie un message à la thread de travail que le travail doit être annulé via BackgroundWorker.CancellationPending. Votre DoWork délégué qui est a couru dans le fond il faut vérifier périodiquement cette propriété et de gérer l'annulation elle-même.

La partie délicate est que votre DoWork délégué est probablement le blocage, ce qui signifie que le travail que vous faites sur votre source de données doit se terminer avant que vous pouvez faire autre chose (comme vérifier CancellationPending). Vous pouvez avoir besoin de déplacer votre travail réel pour encore un autre async délégué (ou peut-être mieux encore, de soumettre le travail à l' ThreadPool), et votre principale thread de scrutin jusqu'à ce thread déclenche un état d'attente, OU il détecte CancellationPending.

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancelasync.aspx

http://www.codeproject.com/KB/cpp/BackgroundWorker_Threads.aspx

34voto

jenovachild Points 166

Si vous ajoutez une boucle entre la CancelAsync() et le RunWorkerAsync() comme il permettra de résoudre votre problème

 private void combobox2_TextChanged(object sender, EventArgs e)
 {
     if (cmbDataSourceExtractor.IsBusy)
        cmbDataSourceExtractor.CancelAsync();

     while(cmbDataSourceExtractor.IsBusy)
        Application.DoEvents();

     var filledComboboxValues = new FilledComboboxValues{ V1 = combobox1.Text,
        V2 = combobox2.Text};
     cmbDataSourceExtractor.RunWorkerAsync(filledComboboxValues );
  }

La boucle while avec l'appel à Candidature.DoEvents() va hault l'exécution de votre nouveau thread de travail jusqu'à ce que l'on a annulé correctement, gardez à l'esprit que vous devez toujours gérer l'annulation de votre thread de travail. Avec quelque chose comme:

 private void cmbDataSourceExtractor_DoWork(object sender, DoWorkEventArgs e)
 {
      if (this.cmbDataSourceExtractor.CancellationPending)
      {
          e.Cancel = true;
          return;
      }
      // do stuff...
 }

L'Application.DoEvents() dans le premier extrait de code continuera à traiter votre GUI threads message de la file d'attente de sorte que le même annuler et mettre à jour les cmbDataSourceExtractor.IsBusy propriété seront toujours traitées (si vous simplement ajouté une continuer au lieu de l'Application.DoEvents() de la boucle de verrouillage de la thread GUI dans un état occupé et ne permettrait pas de traiter l'évènement pour mettre à jour le cmbDataSourceExtractor.IsBusy)

6voto

Daniel Gehriger Points 4101

Vous devrez utiliser un indicateur partagé entre le thread principal et BackgroundWorker, tel que BackgroundWorker.CancellationPending . Lorsque vous souhaitez que BackgroundWorker se ferme, définissez simplement l'indicateur à l'aide de BackgroundWorker.CancelAsync ().

MSDN a un exemple: http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.cancellationpending.aspx

2voto

kawa Points 100

MON exemple DoWork est ci-dessous:

     DoLengthyWork();

    //this is never executed
    if(bgWorker.CancellationPending)
    {
        MessageBox.Show("Up to here? ...");
        e.Cancel = true;
    }
 

dans DoLenghtyWork:

 public void DoLenghtyWork()
{
    OtherStuff();
    for(int i=0 ; i<10000000; i++) 
    {  int j = i/3; }
}
 

dans OtherStuff ():

 public void OtherStuff()
{
    for(int i=0 ; i<10000000; i++) 
    {  int j = i/3; }
}
 

Ce que vous voulez faire, c'est modifier DoLenghtyWork et OtherStuff () afin qu'ils deviennent:

 public void DoLenghtyWork()
{
    if(!bgWorker.CancellationPending)
    {              
        OtherStuff();
        for(int i=0 ; i<10000000; i++) 
        {  
             int j = i/3; 
        }
    }
}

public void OtherStuff()
{
    if(!bgWorker.CancellationPending)
    {  
        for(int i=0 ; i<10000000; i++) 
        {  
            int j = i/3; 
        }
    }
}
 

1voto

Bas Bossink Points 4124

Le problème est dû au fait que cmbDataSourceExtractor.CancelAsync() est une méthode asynchrone, l'opération Cancel n'est pas encore terminée lorsque cmdDataSourceExtractor.RunWorkerAsync(...) sorti. Vous devez attendre que cmdDataSourceExtractor terminé avant d'appeler à nouveau RunWorkerAsync . Comment faire cela est expliqué dans cette question SO .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X