2017-08-21 10 views
2

Je travaille avec un formulaire WinForm à partir duquel tous les processus dont j'ai besoin sont pilotés. Maintenant, j'essaie d'intégrer un BackgroundWorker avec un ProgressBar et un bouton d'annulation dans mon code. Je veux que ce soit localement autour de mon code et non dans une méthode séparée. Pour tester cela, un nouveau formulaire est créé avec une barre de progression (pas encore active) et un bouton pour arrêter une boucle for. Cependant, le code ne fonctionne pas (et la barre de progression n'est même pas encore incluse). Le formulaire se fige immédiatement (voir image), donc je ne peux pas tester le bouton d'annulation. La boucle for est cependant exécutée et "Done: " + l.ToString() est affichée. Comment puis-je resoudre ceci?Gel de formulaire à l'aide de BackgroundWorker

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true;  
} 

public void testcancel() // Test method which is triggered manually 
{ 
    int l = 0; 

    MetingProgress metingProgress = new MetingProgress(); 
    metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement); 

    BackgroundWorker worker = new BackgroundWorker(); 
    worker.WorkerSupportsCancellation = true; 
    worker.DoWork += (sender, args) => 
    {      
     for (int k = 0; k < 10; k++) 
     { 
      Thread.Sleep(1000); 
      l++; 

      if (worker.CancellationPending) 
       break; 
     } 

     MessageBox.Show("Done: " + l.ToString()); 

    }; 
    worker.RunWorkerAsync(); 

    while (worker.IsBusy) 
    { 
     if (stopMeas) 
      worker.CancelAsync(); 
    } 

    metingProgress.Dispose(); 
    MessageBox.Show("All done"); 

} 

enter image description here

+0

Oui, j'ai déjà mon code pour les différentes tâches qui vont devoir aller dans le backgroundworker donc je voudrais avoir une configuration comme dans mon exemple. La boucle for-like ressemble aux tâches à effectuer. – 10a

+0

ok Je vois, alors 'testcancel' est en fait une méthode pour simplement déclencher le thread et le laisser tourner en arrière-plan, et c'est en fait tout à faire.Puisque le thread fonctionnera plus longtemps, vous ne devriez vraiment pas essayer de gérer la disposition du formulaire dans cette méthode! ce serait le travail de la méthode du fil. aussi la ligne 'MessageBox.Show (" All done ");' appartient à l'événement 'DoWork', puisque seul le thread lui-même sait quand le travail est terminé, pas la méthode qui démarre le thread –

Répondre

4

La forme gèle immédiatement

c'est parce que vous avez un while encore en cours d'exécution sur le thread principal! Donc le formulaire ne sera pas réactif. C'est ce qu'on appelle l'attente de Buisy. Vous ne serez pas en mesure d'appeler la méthode CancelAsync.

Une solution pourrait être d'enlever la boucle while et placez l'annuler appel dans le code d'événement bouton:

void stopMeasurement(object sender, EventArgs e) 
{ 
    stopMeas = true; 
    worker.CancelAsync(); 

} 

Ce que vous avez fait est essentiellement: vous avez créé un second jeton d'annulation. Donc, une autre possibilité pourrait être d'utiliser uniquement stopMeas pour annuler l'opération de fond:

worker.DoWork += (sender, args) => 
{      
    for (int k = 0; k < 10; k++) 
    { 
     Thread.Sleep(1000); 
     l++; 

     if (stopMeas) 
      break; 
    } 

    string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!"; 
    MessageBox.Show(mes); 

}; 

EDIT: aussi cette ligne:

metingProgress.Dispose(); 

pourrait conduire à une exception ObjectDisposed. Si le processus d'arrière-plan est toujours en cours d'exécution et tentez de mettre à jour votre barre de progression et que vous disposez déjà du formulaire. Vous devriez enlever cette ligne et la laisser au garbage collector.

+0

Supprimer la boucle? Mais alors il ne reste rien à l'intérieur du travailleur? – 10a

+0

Il parle de la boucle 'while (worker.IsBusy)'. – Fildor

+0

Oui, je viens de le voir et ça marche maintenant. – 10a

4

Ce code est votre problème:

while (worker.IsBusy) 
{ 
    if (stopMeas) 
     worker.CancelAsync(); 
} 

Votre GUI-fil est dans cette boucle jusqu'à ce que votre travailleur se fait. Vous devez rendre votre instance de travail accessible depuis EventHandler et appeler worker.CancelAsync() à partir de là.


En dehors, je voudrais personnellement améliorer le code en 2 étapes:

  1. Déplacer l'ensemble BackgroundWorker dans la classe MetingProgress et faire son constructeur prendre un délégué pour la mise en œuvre réelle de travail.

  2. Utilisez TAP (modèle asynchrone de tâches), c'est-à-dire async/await Tâche avec Progress et CancellationToken.

+0

J'essaie de développer mon code étape par étape et de comprendre comment cela fonctionne exactement, donc je vais probablement finir avec quelque chose que vous suggérez. – 10a

+2

La transition vers TAP a vraiment besoin de temps pour vous envelopper. Prenez votre temps mais ça vaut vraiment la peine d'essayer. Le faire étape par étape est ce que vous pouvez faire de mieux pour apprendre. Bonne chance à toi! – Fildor