Question

Considérez la méthode hypothétique d'un objet qui remplit des fonctions pour vous:

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.CancelAsync();

        //todo: Figure out a way to wait for BackgroundWorker to be cancelled.
    }
}

Comment peut-on attendre qu'un BackgroundWorker soit terminé?

Par le passé, les gens ont essayé:

while (_worker.IsBusy)
{
    Sleep(100);
}

Mais cette impasse , car IsBusy n'est effacé qu'après la gestion de l'événement RunWorkerCompleted , et cet événement ne peut pas être traité jusqu'à ce que l'application devienne inactive. L'application ne reste pas inactive jusqu'à ce que le travail soit terminé. (De plus, c'est une boucle occupée - dégoûtante.)

D'autres ont suggéré de le masquer dans:

while (_worker.IsBusy)
{
    Application.DoEvents();
}

Le problème, c’est que Application.DoEvents () entraîne le traitement des messages actuellement dans la file d’attente, ce qui entraîne des problèmes de ré-entrée (.NET n’est pas réentrant).

J'espère utiliser une solution impliquant des objets de synchronisation d'événements, dans laquelle le code attend un événement - défini par les gestionnaires d'événements RunWorkerCompleted du travailleur. Quelque chose comme:

Event _workerDoneEvent = new WaitHandle();

public void CancelDoingStuff()
{
    _worker.CancelAsync();
    _workerDoneEvent.WaitOne();
}

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    _workerDoneEvent.SetEvent();
}

Mais je suis de retour dans l'impasse: le gestionnaire d'événements ne peut pas être exécuté tant que l'application n'est pas inactive, et l'application ne le devient pas car elle attend un événement.

Alors, comment pouvez-vous attendre la fin d'un BackgroundWorker?

Mettre à jour Les gens semblent être déroutés par cette question. Ils semblent penser que j'utiliserai BackgroundWorker comme:

BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += MyWork;
worker.RunWorkerAsync();
WaitForWorkerToFinish(worker);

Ce n'est pas , ce n'est pas ce que je fais, et ce n'est pas ce qui est demandé ici. Si tel était le cas, il ne servirait à rien d'avoir recours à un agent d'arrière-plan.

Était-ce utile?

La solution

Si je comprends bien votre besoin, vous pouvez faire quelque chose comme ceci (code non testé, mais montre l'idée générale):

private BackgroundWorker worker = new BackgroundWorker();
private AutoResetEvent _resetEvent = new AutoResetEvent(false);

public Form1()
{
    InitializeComponent();

    worker.DoWork += worker_DoWork;
}

public void Cancel()
{
    worker.CancelAsync();
    _resetEvent.WaitOne(); // will block until _resetEvent.Set() call made
}

void worker_DoWork(object sender, DoWorkEventArgs e)
{
    while(!e.Cancel)
    {
        // do something
    }

    _resetEvent.Set(); // signal that worker is done
}

Autres conseils

Il existe un problème avec this réponse. L’interface utilisateur doit continuer à traiter les messages pendant que vous attendez, sinon elle ne sera pas repeinte, ce qui posera un problème si votre agent d’arrière-plan met longtemps à répondre à la demande d’annulation.

Une autre faille est que _resetEvent.Set () ne sera jamais appelé si le thread de travail lève une exception - laissant le thread principal en attente indéfiniment - mais cette faille pourrait facilement être corrigée avec try / enfin bloquer.

Pour cela, vous pouvez afficher une boîte de dialogue modale avec une minuterie qui vérifie à plusieurs reprises si l’agent d’arrière-plan a terminé son travail (ou l’annulation terminée dans votre cas). Une fois que l’agent d’arrière-plan a terminé, la boîte de dialogue modale renvoie le contrôle à votre application. L'utilisateur ne peut pas interagir avec l'interface utilisateur jusqu'à ce que cela se produise.

Une autre méthode (en supposant que vous ayez ouvert au maximum une fenêtre sans modèle) consiste à définir ActiveForm.Enabled = false, puis à effectuer une boucle sur Application, DoEvents jusqu'à ce que l'agent en arrière-plan ait terminé son annulation, après quoi vous pouvez définir ActiveForm.Enabled = true. encore.

Vous êtes presque tous déconcertés par la question et vous ne comprenez pas comment un travailleur est utilisé.

Considérons un gestionnaire d'événements RunWorkerComplete:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;
}

Et tout va bien.

Vient maintenant une situation dans laquelle l'appelant doit abandonner le compte à rebours car il doit exécuter une autodestruction d'urgence de la fusée.

private void BlowUpRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    StartClaxon();
    SelfDestruct();
}

Et il existe également une situation dans laquelle nous devons ouvrir les portes d'accès à la fusée, mais pas pendant le compte à rebours:

private void OpenAccessGates()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

Et enfin, nous devons vider la fusée, mais cela n’est pas autorisé lors du compte à rebours:

private void DrainRocket()
{
    if (worker != null)
    {
        worker.CancelAsync();
        WaitForWorkerToFinish(worker);
        worker = null;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

Sans la possibilité d'attendre qu'un travailleur annule, nous devons déplacer les trois méthodes vers l'événement RunWorkerCompletedEvent:

private void OnRunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (!e.Cancelled)
    {
        rocketOnPad = false;
        label1.Text = "Rocket launch complete.";
    }
    else
    {
        rocketOnPad = true;
        label1.Text = "Rocket launch aborted.";
    }
    worker = null;

    if (delayedBlowUpRocket)
        BlowUpRocket();
    else if (delayedOpenAccessGates)
        OpenAccessGates();
    else if (delayedDrainRocket)
        DrainRocket();
}

private void BlowUpRocket()
{
    if (worker != null)
    {
        delayedBlowUpRocket = true;
        worker.CancelAsync();
        return;
    }

    StartClaxon();
    SelfDestruct();
}

private void OpenAccessGates()
{
    if (worker != null)
    {
        delayedOpenAccessGates = true;
        worker.CancelAsync();
        return;
    }

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

private void DrainRocket()
{
    if (worker != null)
    {
        delayedDrainRocket = true;
        worker.CancelAsync();
        return;
    }

    if (rocketOnPad)
        OpenFuelValves();
}

Maintenant, je pourrais écrire mon code comme ça, mais je ne vais tout simplement pas. Je m'en fiche, je ne le suis tout simplement pas.

Vous pouvez vérifier RunWorkerCompletedEventArgs dans RunWorkerCompletedEventHandler pour connaître l'état de son statut. Succès, annulé ou une erreur.

private void RunWorkerCompletedEventHandler(sender object, RunWorkerCompletedEventArgs e)
{
    if(e.Cancelled)
    {
        Console.WriteLine("The worker was cancelled.");
    }
}

Mettre à jour : pour voir si votre agent a appelé .CancelAsync () à l'aide de ceci:

if (_worker.CancellationPending)
{
    Console.WriteLine("Cancellation is pending, no need to call CancelAsync again");
}

Vous n'attendez pas que le travail en arrière-plan soit terminé. Cela va à l’encontre du but de lancer un thread séparé. Au lieu de cela, vous devez laisser votre méthode se terminer et déplacer tout code dépendant de son achèvement vers un emplacement différent. Vous laissez le travailleur vous dire quand c'est fait et appeler alors le code restant.

Si vous souhaitez attendre que quelque chose soit terminé, utilisez une construction de thread différente qui fournit un WaitHandle.

Pourquoi ne pouvez-vous pas simplement vous lier à l'événement BackgroundWorker.RunWorkerCompleted. Il s'agit d'un rappel qui "se produira lorsque l'opération en arrière-plan est terminée, a été annulée ou a généré une exception."

Je ne comprends pas pourquoi vous voudriez attendre qu'un BackgroundWorker soit terminé; cela ressemble vraiment à l'opposé de la motivation de la classe.

Cependant, vous pouvez démarrer chaque méthode avec un appel à worker.IsBusy et les laisser se fermer si elle est en cours d'exécution.

Je veux juste dire que je suis venu ici parce que j'ai besoin d'un agent d'arrière-plan pour attendre pendant que j'exécutais un processus asynchrone alors que j'étais en boucle, mon correctif était bien plus simple que toutes ces autres choses ^^

foreach(DataRow rw in dt.Rows)
{
     //loop code
     while(!backgroundWorker1.IsBusy)
     {
         backgroundWorker1.RunWorkerAsync();
     }
}

Je pensais partager, car c’est là que j’ai fini en cherchant une solution. De plus, ceci est mon premier article sur le débordement de pile, donc si c'est mauvais, j'adorerais les critiques! :)

Hm, je ne comprends peut-être pas votre question.

L'agent d'arrière-plan appelle l'événement WorkerCompleted une fois que son 'méthode de travail' (la méthode / fonction / sub qui gère le backgroundworker.doWork-event ) est terminé, il n'est donc pas nécessaire de vérifier si le BW est toujours en cours d'exécution. Si vous souhaitez arrêter votre travailleur, vérifiez la propriété en attente d'annulation dans votre" méthode de travail ".

Le flux de travail d'un objet BackgroundWorker nécessite essentiellement de gérer l'événement RunWorkerCompleted pour les cas d'utilisation d'exécution normale et d'annulation d'utilisateur. C'est pourquoi la propriété RunWorkerCompletedEventArgs.Cancelled existe. Pour le faire correctement, vous devez fondamentalement considérer que votre méthode Cancel est une méthode asynchrone en soi.

Voici un exemple:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel;

namespace WindowsFormsApplication1
{
    public class AsyncForm : Form
    {
        private Button _startButton;
        private Label _statusLabel;
        private Button _stopButton;
        private MyWorker _worker;

        public AsyncForm()
        {
            var layoutPanel = new TableLayoutPanel();
            layoutPanel.Dock = DockStyle.Fill;
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.ColumnStyles.Add(new ColumnStyle());
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.AutoSize));
            layoutPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 100));

            _statusLabel = new Label();
            _statusLabel.Text = "Idle.";
            layoutPanel.Controls.Add(_statusLabel, 0, 0);

            _startButton = new Button();
            _startButton.Text = "Start";
            _startButton.Click += HandleStartButton;
            layoutPanel.Controls.Add(_startButton, 0, 1);

            _stopButton = new Button();
            _stopButton.Enabled = false;
            _stopButton.Text = "Stop";
            _stopButton.Click += HandleStopButton;
            layoutPanel.Controls.Add(_stopButton, 1, 1);

            this.Controls.Add(layoutPanel);
        }

        private void HandleStartButton(object sender, EventArgs e)
        {
            _stopButton.Enabled = true;
            _startButton.Enabled = false;

            _worker = new MyWorker() { WorkerSupportsCancellation = true };
            _worker.RunWorkerCompleted += HandleWorkerCompleted;
            _worker.RunWorkerAsync();

            _statusLabel.Text = "Running...";
        }

        private void HandleStopButton(object sender, EventArgs e)
        {
            _worker.CancelAsync();
            _statusLabel.Text = "Cancelling...";
        }

        private void HandleWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                _statusLabel.Text = "Cancelled!";
            }
            else
            {
                _statusLabel.Text = "Completed.";
            }

            _stopButton.Enabled = false;
            _startButton.Enabled = true;
        }

    }

    public class MyWorker : BackgroundWorker
    {
        protected override void OnDoWork(DoWorkEventArgs e)
        {
            base.OnDoWork(e);

            for (int i = 0; i < 10; i++)
            {
                System.Threading.Thread.Sleep(500);

                if (this.CancellationPending)
                {
                    e.Cancel = true;
                    e.Result = false;
                    return;
                }
            }

            e.Result = true;
        }
    }
}

Si vous ne voulez pas vraiment que votre méthode se ferme, nous vous suggérons de placer un indicateur semblable à un AutoResetEvent sur un BackgroundWorker , puis remplacez OnRunWorkerCompleted pour définir l'indicateur. C'est quand même un peu ridicule; Je recommanderais de traiter l'événement d'annulation comme une méthode asynchrone et de faire ce qu'il fait actuellement dans le gestionnaire RunWorkerCompleted .

Je suis un peu en retard pour la fête ici (environ 4 ans), mais qu’en est-il de la configuration d’un thread asynchrone qui puisse gérer une boucle occupée sans verrouiller l’UI, le rappel de ce thread sera alors la confirmation que le BackgroundWorker a fini d'annuler?

Quelque chose comme ça:

class Test : Form
{
    private BackgroundWorker MyWorker = new BackgroundWorker();

    public Test() {
        MyWorker.DoWork += new DoWorkEventHandler(MyWorker_DoWork);
    }

    void MyWorker_DoWork(object sender, DoWorkEventArgs e) {
        for (int i = 0; i < 100; i++) {
            //Do stuff here
            System.Threading.Thread.Sleep((new Random()).Next(0, 1000));  //WARN: Artificial latency here
            if (MyWorker.CancellationPending) { return; } //Bail out if MyWorker is cancelled
        }
    }

    public void CancelWorker() {
        if (MyWorker != null && MyWorker.IsBusy) {
            MyWorker.CancelAsync();
            System.Threading.ThreadStart WaitThread = new System.Threading.ThreadStart(delegate() {
                while (MyWorker.IsBusy) {
                    System.Threading.Thread.Sleep(100);
                }
            });
            WaitThread.BeginInvoke(a => {
                Invoke((MethodInvoker)delegate() { //Invoke your StuffAfterCancellation call back onto the UI thread
                    StuffAfterCancellation();
                });
            }, null);
        } else {
            StuffAfterCancellation();
        }
    }

    private void StuffAfterCancellation() {
        //Things to do after MyWorker is cancelled
    }
}

Cela permet essentiellement de lancer un autre thread à exécuter en arrière-plan qui attend simplement dans sa boucle occupée pour voir si le MyWorker est terminé. Une fois que MyWorker a terminé, le thread se ferme et nous pouvons utiliser le code AsyncCallback pour exécuter la méthode dont nous avons besoin pour suivre l'annulation réussie. Cela fonctionnera comme un événement psuedo. . Dans la mesure où cela est distinct du thread d'interface utilisateur, il ne verrouillera pas l'interface utilisateur tant que nous attendrons que MyWorker ait terminé de s'annuler. Si votre intention est vraiment de verrouiller et d’attendre l’annulation, cela ne vous servira à rien, mais si vous voulez juste attendre pour pouvoir lancer un autre processus, cela fonctionnera sans problème.

Je sais que c'est vraiment tard (5 ans), mais vous cherchez à utiliser un thread et un SynchronizationContext . Vous devez regrouper les appels d'interface utilisateur dans le fil d'interface utilisateur "à la main". plutôt que de laisser le Framework le faire automatiquement comme par magie.

Ceci vous permet d'utiliser un fil que vous pouvez attendre si besoin est.

Imports System.Net
Imports System.IO
Imports System.Text

Public Class Form1
   Dim f As New Windows.Forms.Form
  Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
   BackgroundWorker1.WorkerReportsProgress = True
    BackgroundWorker1.RunWorkerAsync()
    Dim l As New Label
    l.Text = "Please Wait"
    f.Controls.Add(l)
    l.Dock = DockStyle.Fill
    f.StartPosition = FormStartPosition.CenterScreen
    f.FormBorderStyle = Windows.Forms.FormBorderStyle.None
    While BackgroundWorker1.IsBusy
        f.ShowDialog()
    End While
End Sub




Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork

    Dim i As Integer
    For i = 1 To 5
        Threading.Thread.Sleep(5000)
        BackgroundWorker1.ReportProgress((i / 5) * 100)
    Next
End Sub

Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
    Me.Text = e.ProgressPercentage

End Sub

 Private Sub BackgroundWorker1_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles BackgroundWorker1.RunWorkerCompleted

    f.Close()

End Sub

End Class

La solution de Fredrik Kalseth à ce problème est la meilleure que j'ai trouvée jusqu'à présent. D'autres solutions utilisent Application.DoEvent () , ce qui peut poser problème ou tout simplement ne pas fonctionner. Laissez-moi jeter sa solution dans une classe réutilisable. BackgroundWorker n'étant pas scellé, nous pouvons en déduire notre classe:

public class BackgroundWorkerEx : BackgroundWorker
{
    private AutoResetEvent _resetEvent = new AutoResetEvent(false);
    private bool _resetting, _started;
    private object _lockObject = new object();

    public void CancelSync()
    {
        bool doReset = false;
        lock (_lockObject) {
            if (_started && !_resetting) {
                _resetting = true;
                doReset = true;
            }
        }
        if (doReset) {
            CancelAsync();
            _resetEvent.WaitOne();
            lock (_lockObject) {
                _started = false;
                _resetting = false;
            }
        }
    }

    protected override void OnDoWork(DoWorkEventArgs e)
    {
        lock (_lockObject) {
            _resetting = false;
            _started = true;
            _resetEvent.Reset();
        }
        try {
            base.OnDoWork(e);
        } finally {
            _resetEvent.Set();
        }
    }
}

Avec des indicateurs et un verrouillage correct, nous nous assurons que _resetEvent.WaitOne () ne soit vraiment appelé que si du travail a été démarré, sinon _resetEvent.Set (); pourrait ne jamais avoir été appelé!

Try-finally garantit que _resetEvent.Set (); sera appelé, même si une exception doit se produire dans notre gestionnaire DoWork. Sinon, l'application pourrait se figer pour toujours lors de l'appel de CancelSync !

.

Nous l'utilisons comme ceci:

BackgroundWorkerEx _worker;

void StartWork()
{
    StopWork();
    _worker = new BackgroundWorkerEx { 
        WorkerSupportsCancellation = true,
        WorkerReportsProgress = true
    };
    _worker.DoWork += Worker_DoWork;
    _worker.ProgressChanged += Worker_ProgressChanged;
}

void StopWork()
{
    if (_worker != null) {
        _worker.CancelSync(); // Use our new method.
    }
}

private void Worker_DoWork(object sender, DoWorkEventArgs e)
{
    for (int i = 1; i <= 20; i++) {
        if (worker.CancellationPending) {
            e.Cancel = true;
            break;
        } else {
            // Simulate a time consuming operation.
            System.Threading.Thread.Sleep(500);
            worker.ReportProgress(5 * i);
        }
    }
}

private void Worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    progressLabel.Text = e.ProgressPercentage.ToString() + "%";
}

Vous pouvez également ajouter un gestionnaire à l'événement RunWorkerCompleted , comme indiqué ici:
& nbsp; & nbsp; & nbsp; & nbsp; & nbsp; Classe BackgroundWorker (documentation Microsoft) .

La fermeture du formulaire ferme mon fichier journal ouvert. Mon agent d’arrière-plan écrit ce fichier journal, je ne peux donc pas laisser MainWin_FormClosing () se terminer tant que mon agent d’arrière-plan n’est pas terminé. Si je n'attends pas la fin de mon travailleur d'arrière-plan, des exceptions se produisent.

Pourquoi est-ce si difficile?

Un simple Thread.Sleep (1500) fonctionne, mais il retarde l'arrêt (s'il est trop long) ou provoque des exceptions (s'il est trop court).

Pour arrêter juste après la fin du travail de l’arrière-plan, utilisez simplement une variable. Cela fonctionne pour moi:

private volatile bool bwRunning = false;

...

private void MainWin_FormClosing(Object sender, FormClosingEventArgs e)
{
    ... // Clean house as-needed.

    bwInstance.CancelAsync();  // Flag background worker to stop.
    while (bwRunning)
        Thread.Sleep(100);  // Wait for background worker to stop.
}  // (The form really gets closed now.)

...

private void bwBody(object sender, DoWorkEventArgs e)
{
    bwRunning = true;

    BackgroundWorker bw = sender as BackgroundWorker;

    ... // Set up (open logfile, etc.)

    for (; ; )  // infinite loop
    {
        ...
        if (bw.CancellationPending) break;
        ...
    } 

    ... // Tear down (close logfile, etc.)

    bwRunning = false;
}  // (bwInstance dies now.)

Vous pouvez utiliser l’événement RunWorkerCompleted. Même si vous avez déjà ajouté un gestionnaire d'événements pour _worker, vous pouvez en ajouter un autre qu'ils exécuteront dans l'ordre dans lequel ils ont été ajoutés.

public class DoesStuff
{
    BackgroundWorker _worker = new BackgroundWorker();

    ...

    public void CancelDoingStuff()
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => 
        {
            // do whatever you want to do when the cancel completes in here!
        });
        _worker.CancelAsync();
    }
}

cela peut être utile si vous avez plusieurs raisons pour lesquelles une annulation peut se produire, rendant la logique d'un seul gestionnaire RunWorkerCompleted plus compliquée que vous le souhaitez. Par exemple, annuler lorsqu'un utilisateur essaie de fermer le formulaire:

void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
    if (_worker != null)
    {
        _worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler((sender, e) => this.Close());
        _worker.CancelAsync();
        e.Cancel = true;
    }
}

J'utilise la méthode async et wait pour attendre que le travailleur termine son travail:

    public async Task StopAsync()
    {
        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);
    }

et dans la méthode DoWork :

    public async Task DoWork()
    {
        _isBusy = true;
        while (!_worker.CancellationPending)
        {
            // Do something.
        }
        _isBusy = false;
    }

Vous pouvez également encapsuler le code pendant que la boucle dans DoWork avec try ... catch pour définir _isBusy false à l'exception. Ou bien, cochez simplement _worker.IsBusy dans la StopAsync en boucle.

Voici un exemple d'implémentation complète:

class MyBackgroundWorker
{
    private BackgroundWorker _worker;
    private bool _isBusy;

    public void Start()
    {
        if (_isBusy)
            throw new InvalidOperationException("Cannot start as a background worker is already running.");

        InitialiseWorker();
        _worker.RunWorkerAsync();
    }

    public async Task StopAsync()
    {
        if (!_isBusy)
            throw new InvalidOperationException("Cannot stop as there is no running background worker.");

        _worker.CancelAsync();

        while (_isBusy)
            await Task.Delay(1);

        _worker.Dispose();
    }

    private void InitialiseWorker()
    {
        _worker = new BackgroundWorker
        {
            WorkerSupportsCancellation = true
        };
        _worker.DoWork += WorkerDoWork;
    }

    private void WorkerDoWork(object sender, DoWorkEventArgs e)
    {
        _isBusy = true;
        try
        {
            while (!_worker.CancellationPending)
            {
                // Do something.
            }
        }
        catch
        {
            _isBusy = false;
            throw;
        }

        _isBusy = false;
    }
}

Pour arrêter le travailleur et attendre qu'il soit terminé:

await myBackgroundWorker.StopAsync();

Les problèmes avec cette méthode sont les suivants:

  1. Vous devez utiliser des méthodes asynchrones jusqu'au bout.
  2. wait Task.Delay est inexact. Sur mon PC, Task.Delay (1) attend environ 20 ms.

oh mec, certaines d’entre elles sont devenues ridiculement complexes. tout ce que vous devez faire est de vérifier la propriété BackgroundWorker.CancellationPending dans le gestionnaire DoWork. vous pouvez le vérifier à tout moment. une fois qu'il est en attente, définissez e.Cancel = True et libérez-vous de la méthode.

// méthode ici Worker_DoWork (expéditeur d'objet, DoWorkEventArgs e) privé void {     BackgroundWorker bw = (expéditeur en tant que BackgroundWorker);

// do stuff

if(bw.CancellationPending)
{
    e.Cancel = True;
    return;
}

// do other stuff

}

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top