Programing

BackgroundWorker가 취소 될 때까지 기다리는 방법은 무엇입니까?

lottogame 2020. 7. 16. 08:08
반응형

BackgroundWorker가 취소 될 때까지 기다리는 방법은 무엇입니까?


당신을 위해 물건을 만드는 가상의 방법을 고려하십시오 :

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

    ...

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

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

BackgroundWorker가 완료 될 때까지 어떻게 기다릴 수 있습니까?


과거에는 사람들이 시도했습니다 :

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

그러나 이 교착 상태 때문에 IsBusy때까지 해제되지 않은 RunWorkerCompleted이벤트가 처리되며, 응용 프로그램이 유휴 상태가 될 때까지 해당 이벤트가 처리되지 수 있습니다. 작업자가 완료 될 때까지 응용 프로그램이 유휴 상태가되지 않습니다. (또한, 그것은 바쁜 루프입니다-역겨운.)

다른 사람들은 그것을 제안하는 것을 제안했습니다.

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

문제는 Application.DoEvents()현재 큐에있는 메시지가 처리되어 재진입 문제 발생 한다는 것입니다 (.NET은 재진입 할 ​​수 없음).

코드 가 이벤트를 기다리는 이벤트 동기화 객체와 관련된 솔루션을 사용 하려고합니다. 워커의 RunWorkerCompleted이벤트 핸들러는 설정합니다. 다음과 같은 것 :

Event _workerDoneEvent = new WaitHandle();

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

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

그러나 교착 상태로 돌아갑니다. 응용 프로그램이 유휴 상태가 될 때까지 이벤트 처리기를 실행할 수 없으며 응용 프로그램이 이벤트를 대기 중이기 때문에 유휴 상태가 아닙니다.

그러면 BackgroundWorker가 완료 될 때까지 어떻게 기다릴 수 있습니까?


업데이트 사람들은이 질문에 의해 혼동 될 것으로 보인다. 그들은 내가 BackgroundWorker를 다음과 같이 사용할 것이라고 생각하는 것 같습니다.

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

없습니다 입니다, 그것을 하지 내가 뭘하는지, 그리고는 하지 여기에 요구되는 내용. 이 경우에는 백그라운드 작업자를 사용할 필요가 없습니다.


귀하의 요구 사항을 올바르게 이해하면 다음과 같이 할 수 있습니다 (코드 테스트되지 않았지만 일반적인 아이디어를 보여줍니다).

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
}

응답에 문제가 있습니다. 대기 중 UI는 메시지를 계속 처리해야합니다. 그렇지 않으면 다시 페인트하지 않습니다. 이는 백그라운드 작업자가 취소 요청에 응답하는 데 시간이 오래 걸리는 경우 문제가됩니다.

두 번째 결함은 _resetEvent.Set()작업자 스레드가 예외를 던지면 호출되지 않습니다. 메인 스레드가 무기한 대기 상태로 남아 있지만이 결함은 try / finally 블록으로 쉽게 수정할 수 있습니다.

이를 수행하는 한 가지 방법은 백그라운드 작업자가 작업을 완료했는지 (또는 사용자의 경우 취소를 완료했는지) 반복적으로 확인하는 타이머가있는 모달 대화 상자를 표시하는 것입니다. 백그라운드 워커가 완료되면 모달 대화 상자가 컨트롤을 응용 프로그램에 반환합니다. 이 문제가 발생할 때까지 사용자는 UI와 상호 작용할 수 없습니다.

다른 방법은 (모달리스 창이 최대 하나 열려 있다고 가정하면) ActiveForm.Enabled = false를 설정 한 다음 백그라운드 작업자가 취소를 완료 할 때까지 Application, DoEvents를 반복 한 후 ActiveForm.Enabled = true를 다시 설정할 수 있습니다.


거의 모든 사람들이이 질문에 혼란을 겪고 있으며 노동자가 어떻게 사용되는지 이해하고 있지 않습니다.

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

그리고 모두 좋다.

이제 발신자가 로켓의 비상 자체 ​​파괴를 실행해야하므로 카운트 다운을 중단해야하는 상황이 발생합니다.

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

    StartClaxon();
    SelfDestruct();
}

또한 카운트 다운을 수행하지 않고 로켓에 대한 액세스 게이트를 열어야하는 상황도 있습니다.

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

    if (!rocketOnPad)
        DisengageAllGateLatches();
}

마지막으로 로켓에 연료를 공급해야하지만 카운트 다운 중에는 허용되지 않습니다.

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

    if (rocketOnPad)
        OpenFuelValves();
}

작업자가 취소 할 수있는 기능이 없으면 세 가지 메소드를 모두 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();
}

이제 내 코드를 그런 식으로 작성할 수는 있지만 그렇게하지는 않을 것입니다. 상관 없어요.


RunWorkerCompletedEventHandler 에서 RunWorkerCompletedEventArgs체크인 하여 상태를 확인할 수 있습니다 . 성공, 취소 또는 오류

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

업데이트 : 작업자가 다음을 사용하여 .CancelAsync ()를 호출했는지 확인하십시오.

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

당신은 하지 않는 전체의 배경 노동자 기다립니다. 그것은 별도의 스레드를 시작하려는 목적을 거의 능가합니다. 대신 메소드를 완료하고 완료에 따라 다른 코드로 코드를 이동해야합니다. 작업자는 작업이 완료되면 알려주고 나머지 코드를 호출합니다.

완료 되기를 기다리는 경우 WaitHandle을 제공하는 다른 스레딩 구성을 사용하십시오.


BackgroundWorker.RunWorkerCompleted 이벤트에 연결할 수없는 이유는 무엇입니까? "백그라운드 작업이 완료되었거나 취소되었거나 예외가 발생하면 발생합니다."라는 콜백입니다.


BackgroundWorker가 완료되기를 기다리는 이유를 이해하지 못합니다. 그것은 실제로 수업에 대한 동기와 정반대입니다.

그러나 worker.IsBusy를 호출하여 모든 메소드를 시작할 수 있으며 실행중인 경우 종료되도록 할 수 있습니다.


루프에있는 동안 비동기 프로세스를 실행하는 동안 대기 해야하는 백그라운드 작업자가 필요하기 때문에 여기에 왔다고 말하고 싶습니다.

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

이것이 내가 솔루션을 검색하는 동안 끝났기 때문에 내가 공유 할 것이라고 생각했습니다. 또한, 이것은 스택 오버플로에 대한 첫 번째 게시물이므로 나쁜 점이나 비평가를 좋아하는 경우! :)


흠 .. 당신의 질문이 옳지 않을 수도 있습니다

백그라운드 워커는 자신의 'workermethod'( backgroundworker.doWork-event 를 처리하는 메소드 / 함수 / 서브 )가 완료 되면 WorkerCompleted 이벤트를 호출 하므로 BW가 여전히 실행 중인지 확인할 필요가 없습니다. 작업자를 중지하려면 'worker method'내의 취소 보류 속성을 확인하십시오 .


BackgroundWorker객체 의 워크 플로는 기본적으로 RunWorkerCompleted정상적인 실행 및 사용자 취소 사용 사례 모두에 대한 이벤트 를 처리해야합니다 . 이것이 RunWorkerCompletedEventArgs.Cancelled 속성 이 존재하는 이유 입니다. 기본적으로이를 올바르게 수행하려면 Cancel 메소드 자체가 비동기 메소드 인 것으로 간주해야합니다.

예를 들면 다음과 같습니다.

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

당신이 경우 정말 출구로 방법을 싶지 않아, 나는 같은 플래그를 두는 게 좋을 것 AutoResetEvent유래에 BackgroundWorker다음 오버라이드 (override), OnRunWorkerCompleted플래그를 설정할 수 있습니다. 그래도 여전히 일종의 혼잡입니다. cancel 이벤트를 비동기 메서드처럼 처리하고 현재 RunWorkerCompleted처리기 에서 수행하는 작업을 수행하는 것이 좋습니다 .


나는 파티 (약 4 년)에 조금 늦었지만 UI를 잠그지 않고 바쁜 루프를 처리 할 수있는 비동기 스레드를 설정 한 다음 해당 스레드의 콜백을 통해 BackgroundWorker가 취소를 완료했는지 확인합니다. ?

이 같은:

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

본질적으로 이것이하는 일은 다른 스레드를 백그라운드에서 실행하여 바쁜 루프에서 기다렸다가 MyWorker완료 되었는지 확인하는 것 입니다. 일단 MyWorker취소가 끝나면 스레드가 종료되고 AsyncCallback성공적인 취소를 수행하는 데 필요한 모든 메소드를 실행하는 데 사용할 수 있습니다 . 유사 이벤트처럼 작동합니다. 이것은 UI 스레드와 별개이므로 MyWorker취소가 완료 되기를 기다리는 동안 UI를 잠그지 않습니다 . 당신의 의도가 실제로 잠금을 취소하고 취소를 기다리는 것이라면 이것은 쓸모가 없지만 기다리기를 원한다면 다른 프로세스를 시작할 수 있습니다.


나는 이것이 정말로 늦다는 것을 알고 있지만 (5 년) 당신이 찾고있는 것은 Thread와 SynchronizationContext 를 사용하는 것 입니다. 프레임 워크에서 자동으로 수행하지 않고 UI 호출을 "수동으로"마샬링해야합니다.

이를 통해 필요한 경우 기다릴 수있는 스레드를 사용할 수 있습니다.


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의 해결책은 지금까지 내가 찾은 최고입니다. 다른 솔루션을 사용 Application.DoEvent()하면 문제가 발생하거나 작동하지 않을 수 있습니다. 그의 솔루션을 재사용 가능한 클래스로 캐스트하겠습니다. BackgroundWorker봉인되어 있지 않기 때문에 클래스를 파생시킬 수 있습니다.

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

플래그와 적절한 잠금 기능을 사용 _resetEvent.WaitOne()하면 일부 작업이 시작된 경우에만 호출되고 그렇지 않으면 _resetEvent.Set();호출되지 않을 수 있습니다!

try-finally는 _resetEvent.Set();DoWork-handler에서 예외가 발생하더라도 호출 될 것입니다. 그렇지 않으면 전화를 걸 때 응용 프로그램이 영원히 정지 될 수 있습니다 CancelSync!

우리는 이것을 다음과 같이 사용할 것입니다 :

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

다음 RunWorkerCompleted과 같이 이벤트에 핸들러를 추가 할 수도 있습니다 .
     BackgroundWorker Class (Microsoft documentation) .


양식을 닫으면 열려있는 로그 파일이 닫힙니다. 백그라운드 워커가 해당 로그 파일을 작성하므로 MainWin_FormClosing()백그라운드 워커가 종료 될 때까지 완료 할 수 없습니다 . 백그라운드 워커가 종료되기를 기다리지 않으면 예외가 발생합니다.

왜 이렇게 어려운가요?

간단한 Thread.Sleep(1500)작동하지만 종료가 지연되거나 (너무 긴 경우) 예외가 발생합니다 (너무 짧은 경우).

백그라운드 워커가 종료 된 직후 종료하려면 변수를 사용하십시오. 이것은 나를 위해 일하고 있습니다 :

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

RunWorkerCompleted 이벤트를 피기 백 할 수 있습니다. _worker에 대한 이벤트 핸들러를 이미 추가 했더라도 추가 된 순서대로 실행할 다른 핸들러를 추가 할 수 있습니다.

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

취소가 발생할 수있는 여러 가지 이유가있는 경우 단일 RunWorkerCompleted 처리기의 논리를 원하는 것보다 더 복잡하게 만들면 유용 할 수 있습니다. 예를 들어, 사용자가 양식을 닫으려고 할 때 취소 :

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

나는 async방법을 사용 await하고 작업자가 작업을 마칠 때까지 기다립니다.

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

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

그리고 DoWork방법에서 :

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

또한 캡슐화 수 while에 루프 DoWorktry ... catch세트가 _isBusy있다 false예외에. 또는 단순히 확인 _worker.IsBusy에서 StopAsyncwhile 루프.

전체 구현의 예는 다음과 같습니다.

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

작업자를 중지하고 끝까지 실행될 때까지 기다립니다.

await myBackgroundWorker.StopAsync();

이 방법의 문제점은 다음과 같습니다.

  1. 비동기 메소드를 사용해야합니다.
  2. await Task.Delay가 정확하지 않습니다. 내 PC에서 Task.Delay (1)는 실제로 ~ 20ms를 기다립니다.

오,이 중 일부는 엄청나게 복잡해졌습니다. DoWork 핸들러 내에서 BackgroundWorker.CancellationPending 속성을 확인하기 만하면됩니다. 언제든지 확인할 수 있습니다. 보류중인 경우 e.Cancel = True로 설정하고 메소드에서 제외하십시오.

// 여기 메소드 private void Worker_DoWork (객체 발신자, DoWorkEventArgs e) {BackgroundWorker bw = (BackgroundWorker로 보냄);

// do stuff

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

// do other stuff

}

참고 URL : https://stackoverflow.com/questions/123661/how-to-wait-for-a-backgroundworker-to-cancel

반응형