2010-06-17 14 views
6

Disons que j'ai un travailleur de fond comme celui-ci:Comment faire pour démarrer et arrêter un travailleur de fond en marche en permanence Utilisation d'un bouton

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      while(true) 
      { 
        //Kill zombies 
      } 
     } 

Comment puis-je faire de ce travailleur fond démarrage et d'arrêt à l'aide d'un bouton sur un WinForm?

+3

Hmm, la réponse que vous avez accepté, méfiez-vous - vous inquiétez tout au sujet d'un effet qui seule cause il pas d'arrêter une itération ou deux plus tard, mais en oubliant que vous allez être occupé tout un processeur tourne juste! Au moins mettre un sommeil là-dedans. On dirait que ça marche mais au détriment de tout.Certaines des autres réponses résolvent tous ces problèmes de la façon dont vous êtes supposé les résoudre. – FastAl

Répondre

5
+0

Fonctionne très bien: D – sooprise

+0

"L'accès et la mise à jour d'un booléen est une opération atomique". Est-ce à cause de la barrière de la mémoire? –

+0

Non, atomique signifie que la valeur true ou false sera écrite en une fois. Une barrière de mémoire dicte l'ordre que les écritures ou les lectures de la valeur sont vues par un autre fil. Rien ne dit que la valeur écrite pour _performKilling sera visible dans un autre thread sans un tel mécanisme. L'atomicité signifie que la valeur lue sera toujours vraie ou fausse, cela ne signifie pas que le thread d'arrière-plan verra jamais la valeur écrite la plus récente. –

0

Je n'ai pas testé, je le code quelque part que je vais devoir voir exactement ce que je faisais, mais quelque chose comme ceci est une adaptation de la réponse de Fredrik:

private bool _performKilling; 
private object _lockObject = new object(); 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while(true) 
    { 

     if (_performKilling) 
     { 
      //Kill zombies 
     } 
     else 
     { //We pause until we are woken up so as not to consume cycles 
      Monitor.Wait(_lockObject); 
     } 
    } 
} 

private void StartKilling() 
{ 
    _performKilling = true; 
    Monitor.Pulse(_lockObject); 
} 

private void StopAllThatKilling() 
{ 
    _performKilling = false; 
] 

exemple plus complet de ce modèle ici:

https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs

+0

Ah, bien sûr; belle solution. Je dois être fatigué de ne pas l'avoir inventé depuis le début moi-même;) –

+0

_ "Je n'ai pas testé cela" _ - clairement, puisque les appels à 'Wait() 'et' Pulse() 'jetteront des exceptions, parce que vous n'avez pas pris le moniteur avant d'appeler ces méthodes. –

+0

@PeterDuniho Bien sûr, il n'y a pas de plomberie ici. Était juste destiné à devoir le couple de méthodes que l'on utilise sur un moniteur pour faciliter la pause/non-pause. Plus complet exemple ici: https://github.com/AaronLS/CellularAutomataAsNeuralNetwork/blob/fe9e6b950e5e28d2c99350cb8ff3157720555e14/CellLifeGame1/Modeling.cs – AaronLS

17

peut-être que vous pouvez utiliser un ManualResetEvent comme ça, je di ne débogue pas ça mais vaut le coup. Si cela fonctionne, vous ne serez pas avoir le fil tourner ses roues alors qu'il attend

ManualResetEvent run = new ManualResetEvent(true); 

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    while(run.WaitOne()) 
    { 
     //Kill zombies 
    } 
} 

private void War() 
{ 
    run.Set(); 
} 

private void Peace() 
{ 
    run.Reset(); 
} 
+0

+1 C'est génial de voir une autre âme qui comprend vraiment le filetage .NET. ;) Je pense que l'utilisation d'un WaitHandle est toujours le meilleur choix dans ces situations, car ils prennent en charge intrinsèquement l'atomicité et d'autres problèmes de multithreading pour vous (en supposant que vous choisissez le bon type de WaitHandle). – jrista

+0

'while (true) {if (run.WaitOne()) {}}'? Cela semble un peu redondant, n'est-ce pas? Pourquoi pas 'while (run.WaitOne()) {}'? –

+0

Il est en effet, je pensais que moi-même après la publication, je vais le mettre à jour –

7

Utilisez la méthode CancelAsync.

backgroundworker1.CancelAsync(); 

Dans votre boucle à l'intérieur du thread de travail.

if (backgroundWorker.CancellationPending) return; 

Cela ne se produit pas immédiatement.

+5

+1: Je ne comprends pas pourquoi tout le monde dit de vérifier 'while (_someCustomBoolean)' quand la classe 'BackgroundWorker' a déjà cette fonctionnalité intégrée. –

+1

Une fois l'annulation en attente est définie, je pense que le protocole général est pour le travailleur d'arrière-plan pour arrêter et terminer. Dans cette situation, je crois que les questions demandaient un moyen de commencer et d'arrêter. Le travailleur de l'arrière-plan peut aussi vous trébucher. Si vous utilisez son événement incompleted, ceci est posté à travers le ThreadPool générique de .net, j'ai vu cette impasse avant de dépendre de quoi d'autre est jeté là. –

4

Par arrêt, voulez-vous vraiment dire arrêter ou voulez-vous dire pause? Si vous voulez dire arrêter, alors c'est un morceau de gâteau. Créez un gestionnaire d'événement de clic de bouton pour le bouton que vous voulez être responsable du démarrage du travailleur d'arrière-plan et un gestionnaire d'événements de clic de bouton pour celui responsable de l'arrêter. Sur votre bouton de démarrage, appelez la méthode de travail en arrière-plan qui déclenche l'événement do_work. Quelque chose comme ceci:

private void startButton_Click(System.Object sender, 
    System.EventArgs e) 
{ 
    // Start the asynchronous operation. 
    backgroundWorker1.RunWorkerAsync(); 
} 

Sur votre bouton d'arrêt, faire un appel à la méthode qui définit le travailleur de CancellationPending de fond vrai, comme ceci: Maintenant

private void cancelAsyncButton_Click(System.Object sender, 
    System.EventArgs e) 
{ 
    // Cancel the asynchronous operation. 
    this.backgroundWorker1.CancelAsync(); 
} 

ne pas oublier de vérifier la CancelationPending drapeau à l'intérieur de votre travail. Quelque chose comme ceci:

private void KillZombies(BackgroundWorker worker, DoWorkEventArgs e) 
    { 
     while (true) 
     { 
      if (worker.CancellationPending) 
      { 
       e.Cancel = true; 
      } 
     } 
    } 

Et votre méthode DoWork:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
     { 
      BackgroundWorker worker = sender as BackgroundWorker; 
      KillZombies(worker, e); 
     } 

J'espère que cela peut vous orienter dans la bonne direction. Quelques autres lectures:

http://msdn.microsoft.com/en-us/library/b2zk6580(v=VS.90).aspx

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

http://msdn.microsoft.com/en-us/library/waw3xexc.aspx