Frage

Betrachten wir eine hypothetische Methode eines Objekts, das Material für Sie tut:

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

    ...

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

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

Wie kann man warten für ein Background zu tun?


In der Vergangenheit Menschen haben versucht:

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

Aber diese Deadlocks , weil IsBusy erst gelöscht nach dem RunWorkerCompleted Ereignis behandelt wird, und das Ereignis nicht behandelt bekommen kann, bis geht die Anwendung inaktiv. Die Anwendung wird nicht gehen leer, bis der Arbeiter erfolgt. (Plus, es ist eine geschäftige Schleife -. Ekelhaft)

Andere fügen haben vorgeschlagen, sie in kludging:

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

Das Problem mit diesem ist, dass Application.DoEvents() ist, um Nachrichten zur Zeit in der Warteschlange verursacht, zu verarbeiten, die Ursache Neueintritt Probleme (.NET ist nicht einspringenden).

Ich würde hoffen, dass irgendeine Lösung beteiligt Ereignissynchronisationsobjekte zu verwenden, wobei der Code wartet für eine Veranstaltung -, dass der RunWorkerCompleted Event-Handler-Sets des Arbeitnehmers. So etwas wie:

Event _workerDoneEvent = new WaitHandle();

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

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

Aber ich bin zu dem Deadlock zurück: der Event-Handler kann nicht ausgeführt werden, bis die Anwendung im Leerlauf geht, und die Anwendung wird nicht untätig gehen, weil es für ein Ereignis wartet

.

Wie können Sie also ein Background warten zu beenden?


Aktualisieren Die Menschen scheinen sich mit dieser Frage verwechselt werden. Sie scheinen zu glauben, dass ich den Background verwenden werde als:

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

Das ist nicht es, das ist nicht , was ich tue, und das ist nicht , was hier gefragt wird. Wenn das der Fall wäre, gäbe es keinen Sinn, einen Hintergrund Arbeiter in Verwendung.

War es hilfreich?

Lösung

Wenn ich Ihre Anforderung richtig verstehen, können Sie etwas tun könnte (Code nicht getestet, sondern zeigt die allgemeine Idee):

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
}

Andere Tipps

Es gibt ein Problem mit this Antwort. Die Benutzeroberfläche muss weiterhin Nachrichten zu verarbeiten, während Sie warten, sonst wird es nicht neu aufgebaut, was ein Problem sein, wenn Ihr Hintergrund Arbeiter eine lange Zeit in Anspruch nimmt, um die Löschanforderung zu reagieren.

Ein zweiter Fehler ist, dass _resetEvent.Set() nie, wenn der Arbeiter-Thread eine Ausnahme auslöst genannt werden - den Hauptthread verläßt auf unbestimmte Zeit warten -. Aber diese Fehler leicht mit einem try / finally-Block festgelegt werden könnten

Eine Möglichkeit, dies zu tun, ist ein modaler Dialog angezeigt werden, die einen Zeitgeber hat, der wiederholt überprüft, ob der Hintergrund Arbeiter Arbeit (oder fertig in Ihrem Fall Cancelling) beendet hat. Sobald der Hintergrund Arbeiter beendet ist, steuern die modalen Dialog kehrt in Ihrer Anwendung. Der Benutzer kann nicht mit der Benutzeroberfläche interagieren, bis dies geschieht.

Eine andere Methode (vorausgesetzt, Sie haben maximal eine modale Fenster geöffnet) ist ActiveForm.Enabled = false, dann Schleife auf Anfrage zu setzen, DoEvents, bis der Hintergrund Arbeiter Cancelling beendet ist, nach dem Sie ActiveForm.Enabled = true einstellen wieder.

Fast alle von euch durch die Frage verwirrt sind und nicht verstehen, wie ein Arbeiter verwendet wird.

Betrachten wir ein RunWorkerComplete Ereignishandler:

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;
}

Und alles ist gut.

Jetzt kommt eine Situation, wo der Anrufer den Countdown abzubrechen braucht, weil sie eine Notfall Selbstzerstörung der Rakete ausführen müssen.

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

    StartClaxon();
    SelfDestruct();
}

Und es gibt auch eine Situation, in der wir die Zugangstüren zur Rakete öffnen müssen, aber nicht während eines Countdown zu tun:

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

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

Und schließlich müssen wir die Rakete de-Kraftstoff, aber das ist nicht während eines Countdowns erlaubt:

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

    if (rocketOnPad)
        OpenFuelValves();
}

Ohne die Möglichkeit, für einen Arbeiter zu warten, um zu kündigen, müssen wir alle drei Methoden zum RunWorkerCompletedEvent bewegen:

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();
}

Jetzt kann ich meinen Code so schreiben, aber ich bin einfach nicht würde. Ist mir egal, ich bin einfach nicht.

Sie können in die überprüfen RunWorkerCompletedEventArgs in der RunWorkerCompletedEventHandler , um zu sehen, was der Status war. Erfolg, abgebrochen oder ein Fehler.

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

Aktualisieren : Um zu sehen, wenn Ihr Arbeitnehmer hat .CancelAsync () aufgerufen dies durch die Verwendung:

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

Sie nicht warten, bis der Hintergrund Arbeitnehmer abzuschließen. Das ist ziemlich viel Niederlagen der Zweck einen eigenen Thread der Einführung. Stattdessen sollten Sie Ihre Methode beenden, lassen Sie und beliebigen Code verschieben, die nach Abschluss zu einem anderen Ort abhängt. Sie lassen die Arbeiter Ihnen sagen, wenn es fertig ist, und rufen alle verbleibenden Code dann.

Wenn Sie auf warten für etwas verwendet ein anderes Threading-Konstrukt zu vervollständigen, die ein Waithandle zur Verfügung stellt.

Warum können Sie nicht nur die BackgroundWorker.RunWorkerCompleted Ereignis binden in. Es ist ein Rückruf, wird „auftreten, wenn der Hintergrundvorgang abgeschlossen ist, abgebrochen wurde, oder hat eine Ausnahme ausgelöst.“

Ich verstehe nicht, warum Sie sich für einen Background warten wollen würden zu vervollständigen; es scheint wirklich, wie das genaue Gegenteil der Motivation für die Klasse.

Sie können jedoch jede Methode mit einem Aufruf an worker.IsBusy beginnen und sie beendet, wenn es ausgeführt wird.

Ich will nur sagen, dass ich hierher gekommen, weil ich einen Hintergrund Arbeiter müssen warten, während ich einen asynchronen Prozess ausgeführt wurde, während in einer Schleife, mein fix Weise einfacher, als alle diese anderen Sachen war ^^

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

Nur dachte, ich würde Aktie, weil das ist, wo ich am Ende, während Suche nach einer Lösung. Auch dies ist mein erster Beitrag auf Stack-Überlauf so, wenn seine schlechte oder alles, was ich liebe Kritiker würden! :)

Hm vielleicht bekomme ich bin nicht deine Frage richtig.

Die Background ruft das WorkerCompleted Ereignis einmal seinen 'workermethod' (die Methode / Funktion / sub, die die backgroundworker.doWork-Ereignis ) beendet ist, so gibt es keine Notwendigkeit für die Überprüfung ist, wenn die BW läuft noch. Wenn Sie Ihre Arbeiter stoppen wollen überprüfen Sie die

Der Workflow eines BackgroundWorker Objekt erfordert, dass Sie im Grunde das RunWorkerCompleted Ereignis sowohl für die Verwendung der normalen Ausführung und Benutzer Stornierung Fälle zu behandeln. Aus diesem Grund ist die Eigenschaft RunWorkerCompletedEventArgs.Cancelled existiert. Im Grunde ist dies tut richtig setzt voraus, dass Sie als Ihre Methode Abbrechen ein asynchrones Verfahren an sich zu sein.

Hier ist ein Beispiel:

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;
        }
    }
}

Wenn Sie wirklich wirklich will nicht Ihre Methode, um zu beenden, würde ich vorschlagen, eine Flagge wie ein AutoResetEvent auf einem abgeleiteten BackgroundWorker setze, dann OnRunWorkerCompleted außer Kraft setzen, die die Flagge zu setzen. Es ist immer noch, obwohl Art von kludgy; Ich würde empfehlen, das Ereignis wie ein asynchrones Verfahren abzubrechen Behandlung und tut, was es zur Zeit in dem RunWorkerCompleted Handler zu tun.

Ich bin ein wenig spät, um die Partei hier (ca. 4 Jahre), aber was ist einen asynchronen Thread Einrichtung, die ein Besetzt Schleife ohne Sperren den Benutzeroberfläche umgehen kann, dann den Rückruf hat aus diesem Thread wird die Bestätigung, dass der Background beendet Cancelling?

So etwas wie folgt aus:

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
    }
}

Im Wesentlichen, was dies tut, ist einen anderen Thread abfeuern im Hintergrund laufen zu lassen, die gerade warten darin beschäftigt Schleife zu sehen sind, wenn der MyWorker abgeschlossen hat. Sobald MyWorker beendet hat den Faden Cancelling beendet und wir können es AsyncCallback verwenden, um auszuführen, was Methode, die wir die erfolgreiche Löschung folgen müssen - es wird wie ein Pseudo-Ereignis arbeiten. Da diese aus dem UI-Thread getrennt ist, wird es nicht die Benutzeroberfläche sperren, während wir für MyWorker warten Abbrechen zu beenden. Wenn Ihre Absicht wirklich sperren und warten, bis die abbrechen, dann ist dies nutzlos für Sie, aber wenn Sie nur warten wollen, so können Sie einen anderen Prozess starten dann das funktioniert gut.

Ich weiß, das wirklich spät ist (5 Jahre), aber was Sie suchen ist ein Thread zu verwenden und eine SynchronizationContext . Sie werden UI müssen Marshal „von Hand“ auf dem UI-Thread ruft zurück, anstatt lassen die Rahmen tun es auto-magisch an.

Auf diese Weise können Sie einen Thread verwenden, die Sie warten können, wenn es sein muss.

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

Fredrik Kalseth Lösung für dieses Problem ist das Beste, was ich bisher gefunden habe. Andere Lösungen verwenden Application.DoEvent() die Probleme verursachen können oder einfach nicht funktionieren. Lassen Sie mich seine Lösung in eine wiederverwendbare Klasse gegossen. Da BackgroundWorker nicht abgedichtet ist, können wir unsere Klasse daraus ziehen:

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();
        }
    }
}

Mit Fahnen und korrekte Verriegelung, stellen wir sicher, dass _resetEvent.WaitOne() wirklich nur dann, wenn einige Arbeiten begonnen wurde aufgerufen wird, sonst _resetEvent.Set(); vielleicht nie genannt worden!

Die try-finally sorgt dafür, dass _resetEvent.Set(); genannt werden, auch wenn eine Ausnahme in unseren DoWork-Handler auftreten sollte. Andernfalls könnte die Anwendung für immer einfrieren, wenn CancelSync ruft!

Wir würden es wie folgt verwendet werden:

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() + "%";
}

Sie können auch einen Handler zum RunWorkerCompleted Ereignis hinzufügen, wie hier gezeigt:
Background Klasse < em> (Microsoft-Dokumentation) .

das Formular Beim Schließen meine offene Logfile. Mein Hintergrund Arbeiter schreibt, dass Log-Datei, so kann ich nicht MainWin_FormClosing() Finish bis mein Hintergrund Arbeiter beendet lassen. Wenn ich warte nicht auf meinen Hintergrund Arbeiter zu beenden, Ausnahmen passieren.

Warum ist das so schwer?

Ein einfaches Thread.Sleep(1500) funktioniert, aber es verzögert shutdown (wenn zu lang), oder verursacht Ausnahmen (wenn auch kurz).

So fahren Sie direkt nach dem Hintergrund Arbeiter beendet, nur eine Variable verwenden. Dies funktioniert für mich:

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.)

Sie können aus dem RunWorkerCompleted Ereignisse huckepack. Auch wenn Sie bereits einen Event-Handler für _worker hinzugefügt haben, können Sie eine weitere hinzufügen, ein sie werden in der Reihenfolge ausgeführt, in der sie hinzugefügt wurden.

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();
    }
}

Dies könnte nützlich sein, wenn Sie mehrere Gründe, warum ein auftreten aufheben kann, die Logik eines einzigen RunWorkerCompleted Handler machen komplizierter, als Sie wollen. Zum Beispiel Cancelling, wenn ein Benutzer versucht, das Formular zu schließen:

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

Ich verwende async Methode und await für den Arbeiter zu warten, seine Aufgabe Bearbeitung:

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

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

und in DoWork Methode:

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

Sie können auch die while Schleife in DoWork mit try ... catch kapseln _isBusy ist false auf Ausnahme einzustellen. Oder einfach _worker.IsBusy im StopAsync while-Schleife überprüfen.

Hier ist ein Beispiel für die vollständige Umsetzung:

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;
    }
}

Um die Arbeiter zu stoppen und warten, bis es am Ende läuft:

await myBackgroundWorker.StopAsync();

Die Probleme mit dieser Methode sind:

  1. Sie müssen Asynchron-Methoden den ganzen Weg nutzen.
  2. erwarten Task.Delay ungenau ist. Auf meinem PC, Task.Delay (1) wartet tatsächlich ~ 20 ms.

oh Mann, einige von ihnen haben lächerlich komplex geworden. alles, was Sie tun müssen, ist die BackgroundWorker.CancellationPending Eigenschaft innerhalb der DoWork Handler zu überprüfen. Sie können es jederzeit überprüfen. sobald es ansteht, setzen e.Cancel = True und Bügel aus dem Verfahren.

// Methode hier private void Worker_DoWork (object sender, DoWorkEventArgs e) {     Background bw = (Sender als Background);

// do stuff

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

// do other stuff

}

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top