it-swarm.com.de

Wie kann ich warten, bis ein BackgroundWorker abbricht?

Betrachten Sie eine hypothetische Methode eines Objekts, das für Sie Sachen erledigt:

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, bis ein BackgroundWorker fertig ist?


In der Vergangenheit haben Leute versucht:

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

Aber diese Deadlocks , weil IsBusy erst gelöscht wird, nachdem das RunWorkerCompleted-Ereignis behandelt wurde, und dieses Ereignis nicht behandelt werden kann, bis die Anwendung inaktiv ist. Die Anwendung wird erst dann inaktiv, wenn der Mitarbeiter fertig ist. (Plus, es ist eine belebte Schleife - ekelhaft.)

Andere haben folgende Vorschläge hinzugefügt:

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

Das Problem dabei ist, dass Application.DoEvents() bewirkt, dass Nachrichten, die sich derzeit in der Warteschlange befinden, verarbeitet werden, was zu Problemen beim erneuten Eintreten führt (.NET ist nicht erneut eintritt).

Ich würde hoffen, eine Lösung mit Ereignissynchronisationsobjekten zu verwenden, bei der der Code waets auf ein Ereignis wartet, das von den RunWorkerCompleted-Ereignishandlern des Arbeiters festgelegt wird. 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 wieder im Deadlock: Der Event-Handler kann erst ausgeführt werden, wenn die Anwendung in den Leerlauf geht, und die Anwendung wird nicht in den Leerlauf gehen, da auf ein Ereignis gewartet wird.

Wie können Sie also warten, bis ein BackgroundWorker fertig ist?


Update Die Leute scheinen von dieser Frage verwirrt zu sein. Sie scheinen zu glauben, dass ich den BackgroundWorker folgendermaßen einsetzen werde:

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. In diesem Fall hätte es keinen Sinn, einen Hintergrundarbeiter zu verwenden.

117
Ian Boyd

Wenn ich Ihre Anforderung richtig verstehe, könnten Sie so etwas tun (Code nicht getestet, zeigt aber 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
}
121
Fredrik Kalseth

Es gibt ein Problem mit this response. Während der Wartezeit muss die Benutzeroberfläche die Nachrichten weiter verarbeiten. Andernfalls wird die Oberfläche nicht neu gezeichnet. Dies ist ein Problem, wenn der Hintergrundarbeiter lange Zeit benötigt, um auf die Abbruchanforderung zu antworten.

Ein zweiter Fehler ist, dass _resetEvent.Set() niemals aufgerufen wird, wenn der Worker-Thread eine Ausnahme auslöst. Der Main-Thread wird unbegrenzt warten. Dieser Fehler kann jedoch leicht mit einem try/finally-Block behoben werden.

Eine Möglichkeit, dies zu tun, ist die Anzeige eines modalen Dialogs mit einem Timer, der wiederholt prüft, ob der Hintergrundarbeiter seine Arbeit beendet hat (oder in Ihrem Fall den Abbruch beendet hat). Sobald der Hintergrundarbeiter fertig ist, gibt der modale Dialog die Kontrolle an Ihre Anwendung zurück. Der Benutzer kann erst dann mit der Benutzeroberfläche interagieren, wenn dies geschieht.

Eine andere Methode (vorausgesetzt, Sie haben maximal ein Modellfenster geöffnet) besteht darin, ActiveForm.Enabled = false zu setzen und dann Application, DoEvents zu durchlaufen, bis der Hintergrundarbeiter das Abbrechen beendet hat. Danach können Sie ActiveForm.Enabled = true erneut festlegen.

13
Joe

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

Betrachten Sie einen 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, in der der Anrufer den Countdown abbrechen muss, weil er eine Not-Selbstzerstörung der Rakete durchführen muss.

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

    StartClaxon();
    SelfDestruct();
}

Und es gibt auch eine Situation, in der wir die Zugangstore zur Rakete öffnen müssen, aber nicht während eines Countdowns:

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

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

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

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

    if (rocketOnPad)
        OpenFuelValves();
}

Ohne die Möglichkeit, auf den Abbruch eines Workers zu warten, müssen alle drei Methoden in das RunWorkerCompletedEvent verschoben werden:

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 könnte ich meinen Code so schreiben, aber ich werde einfach nicht. Es ist mir egal, ich bin einfach nicht.

10
Ian Boyd

Sie können unter RunWorkerCompletedEventArgs im RunWorkerCompletedEventHandler nachsehen, wie der Status war. Erfolg abgebrochen oder ein Fehler.

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

Update : So prüfen Sie, ob Ihr Worker .CancelAsync () aufgerufen hat, indem Sie Folgendes verwenden:

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

Sie nicht warten, bis der Hintergrund-Worker fertig ist. Das macht dem Zweck, einen separaten Thread zu starten, so gut wie nichts aus. Stattdessen sollten Sie Ihre Methode beenden lassen und jeden Code, der von der Fertigstellung abhängt, an einen anderen Ort verschieben. Sie lassen sich vom Arbeiter sagen, wann es fertig ist, und rufen dann den verbleibenden Code auf.

Wenn Sie warten möchten, damit etwas abgeschlossen ist, verwenden Sie ein anderes Threading-Konstrukt, das ein WaitHandle bereitstellt.

4
Joel Coehoorn

Warum können Sie nicht einfach in das BackgroundWorker.RunWorkerCompleted-Ereignis einbinden. Es ist ein Rückruf, der "auftritt, wenn die Hintergrundoperation abgeschlossen ist, abgebrochen wurde oder eine Ausnahme ausgelöst hat."

3
Rick Minerich

Ich verstehe nicht, warum Sie auf den Abschluss eines BackgroundWorker warten möchten. Es scheint wirklich das genaue Gegenteil der Motivation für die Klasse zu sein.

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

1
Austin Salonen

Ich möchte nur sagen, dass ich hierher gekommen bin, weil ich einen Hintergrundarbeiter brauche, um zu warten, während ich in einer Schleife einen asynchronen Prozess durchführte

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

Ich dachte nur, ich würde es teilen, denn hier bin ich auf der Suche nach einer Lösung gelandet. Dies ist auch mein erster Beitrag zum Stack-Overflow. Wenn es schlecht ist, würde ich Kritiker lieben! :)

1
Connor Williams

Ich verwende async method und await, um zu warten, bis der Arbeiter seine Arbeit beendet hat:

    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 die while-Schleife auch in DoWork mit try ... catch kapseln, um festzulegen, dass _isBusy in Ausnahmefällen false ist. Oder überprüfen Sie einfach den _worker.IsBusy in der StopAsync while-Schleife.

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

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

So stoppen Sie den Worker und warten, bis er zu Ende ist:

await myBackgroundWorker.StopAsync();

Die Probleme bei dieser Methode sind:

  1. Sie müssen ausschließlich asynchrone Methoden verwenden.
  2. warten Task.Delay ist ungenau. Task.Delay (1) wartet auf meinem PC ca. 20ms.
0
Anthony

Hm, vielleicht bekomme ich deine Frage nicht richtig.

Der backgroundworker ruft das WorkerCompleted-Ereignis auf, sobald seine 'workermethod' (Methode/Funktion/Sub, die das backgroundworker.doWork-event behandelt) abgeschlossen ist, so dass nicht geprüft werden muss, ob das BW noch läuft. Wenn Sie Ihren Worker anhalten möchten, überprüfen Sie die cancel ausstehende Eigenschaft in Ihrer Workermethode.

0
Stephan

Fredrik Kalseths Lösung für dieses Problem ist die beste, die 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 umwandeln. Da BackgroundWorker nicht versiegelt ist, können wir unsere Klasse daraus ableiten:

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 Flags und ordnungsgemäßem Sperren stellen wir sicher, dass _resetEvent.WaitOne() wirklich nur dann aufgerufen wird, wenn eine Arbeit gestartet wurde. Andernfalls wurde _resetEvent.Set(); möglicherweise nie aufgerufen!

Der try-finally stellt sicher, dass _resetEvent.Set(); aufgerufen wird, auch wenn in unserem DoWork-Handler eine Ausnahme auftritt. Andernfalls kann die Anwendung beim Aufruf von CancelSync für immer einfrieren!

Wir würden es so verwenden:

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 dem Ereignis RunWorkerCompleted auch einen Handler hinzufügen, wie hier gezeigt:
BackgroundWorker-Klasse(Microsoft-Dokumentation).

Das Schließen des Formulars schließt meine geöffnete Protokolldatei. Mein Hintergrundarbeiter schreibt diese Protokolldatei, daher kann ich MainWin_FormClosing() erst dann beenden, wenn mein Hintergrundarbeiter beendet wird. Wenn ich nicht auf die Beendigung meines Hintergrundarbeiters warte, kommt es zu Ausnahmen.

Warum ist das so schwer?

Eine einfache Thread.Sleep(1500) funktioniert, verzögert jedoch das Herunterfahren (wenn zu lang), oder verursacht Ausnahmen (falls zu kurz).

Um das Programm sofort nach Beendigung des Hintergrundarbeiters herunterzufahren, verwenden Sie einfach eine Variable. Das 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.)
0
A876

Ich weiß, das ist wirklich spät (5 Jahre), aber Sie suchen einen Thread und einen SynchronizationContext . Sie müssen die Aufrufe der Benutzeroberfläche an den UI-Thread "von Hand" marshallieren, anstatt es dem Framework automatisch zu erlauben.

Dadurch können Sie einen Thread verwenden, auf den Sie bei Bedarf warten können.

0
Tom Padilla
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
0
Nitesh

Sie können das Ereignis RunWorkerCompleted abbrechen. Auch wenn Sie bereits einen Event-Handler für _worker hinzugefügt haben, können Sie einen weiteren hinzufügen, der in der Reihenfolge ausgeführt wird, in der er hinzugefügt wurde.

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 kann nützlich sein, wenn es mehrere Gründe gibt, warum ein Abbruch auftreten kann, wodurch die Logik eines einzelnen RunWorkerCompleted-Handlers komplizierter wird als gewünscht. Abbrechen beispielsweise, 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;
    }
}
0
Shawn Rubie

Der Workflow eines BackgroundWorker-Objekts erfordert grundsätzlich, dass Sie das RunWorkerCompleted-Ereignis sowohl für die normale Ausführung als auch für den Anwendungsabbruch von Benutzern abwickeln. Deshalb existiert die Eigenschaft RunWorkerCompletedEventArgs.Cancelled . Wenn Sie dies richtig machen, müssen Sie Ihre Cancel-Methode als eine asynchrone Methode betrachten.

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 nicht möchten, dass Ihre Methode nicht beendet wird, würde ich vorschlagen, eine Markierung wie AutoResetEvent auf einem abgeleiteten BackgroundWorker zu setzen. Überschreiben Sie OnRunWorkerCompleted, um das Flag zu setzen. Es ist immer noch irgendwie kludig; Ich empfehle, das Cancel-Ereignis wie eine asynchrone Methode zu behandeln und im RunWorkerCompleted-Handler das zu tun, was es gerade tut.

0
OwenP

Ich bin ein bisschen spät zur Party hier (ungefähr 4 Jahre), aber was ist mit dem Einrichten eines asynchronen Threads, der eine ausgelastete Schleife verarbeiten kann, ohne die Benutzeroberfläche zu sperren? ?

Etwas wie das:

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 führt dies dazu, dass ein anderer Thread ausgelöst wird, der im Hintergrund ausgeführt wird und nur wartet, ob die MyWorker abgeschlossen ist. Sobald MyWorker das Abbrechen beendet hat, wird der Thread beendet und wir können AsyncCallback verwenden, um die Methode auszuführen, die wir für das erfolgreiche Abbrechen benötigen - es funktioniert wie ein Psuedo-Ereignis. Da dies vom UI-Thread getrennt ist, wird die Benutzeroberfläche nicht gesperrt, während wir warten, bis MyWorker den Abbruch abgeschlossen hat. Wenn Sie wirklich abschließen möchten und auf den Abbruch warten, ist dies für Sie nutzlos. Wenn Sie jedoch nur warten möchten, damit Sie einen anderen Prozess starten können, funktioniert dies gut.

0
Infotekka