如何等待BackgroundWorker取消?


125

考虑一个 为您做事的对象假设方法:

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

这是不是它,那是不是我在做什么,那就是没有什么被要求在这里。如果真是这样,那么使用后台工作人员将毫无意义。

Answers:


130

如果我了解您的要求正确,则可以执行以下操作(代码未经测试,但显示了总体思路):

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
}

7
如果后台工作人员花费很长时间取消操作,这将阻止UI(例如,不重新绘制)。最好使用以下其中一种停止与UI的交互:stackoverflow.com/questions/123661/…–
Joe Joe

1
+1医生的命令...虽然我同意@Joe的要求,但取消请求可能需要一秒钟以上的时间。
dotjoe

当在WaitOne之前处理CancelAsync时会发生什么?还是“取消”按钮仅起作用一次。
CodingBarfield

6
我需要检查((BackgroundWorker)sender).CancellationPending以获得取消事件
Luuk

1
正如Luuk所提到的,必须检查的不是Cancel属性,而是CancellationPending。其次,正如Joel Coehoorn所提到的那样,等待线程终止将首先破坏使用线程的目的。
明智船长

15

响应有问题。UI需要在您等待时继续处理消息,否则它将不会重新绘制,如果后台工作人员花很长时间响应取消请求,这将是一个问题。

第二个缺点是 _resetEvent.Set()如果工作线程引发异常-将使主线程无限期地等待-将永远不会被调用-但是,可以使用try / finally块轻松修复此缺陷。

一种方法是显示一个模态对话框,其中包含一个计时器,该计时器可反复检查后台工作人员是否已完成工作(或您的情况下完成取消)。后台工作人员完成操作后,模态对话框将控制权返回给您的应用程序。在这种情况发生之前,用户无法与UI进行交互。

另一种方法(假设您最多打开一个无模式窗口)是将ActiveForm.Enabled = false设置为,然后在Application,DoEvents上循环运行,直到后台工作人员完成取消为止,然后可以再次将ActiveForm.Enabled = true设置为。


5
这可能是一个问题,但它从根本上被接受为“如何等待 BackgroundWorker取消”问题的一部分。等待意味着您等待,您什么都不做。这也包括处理消息。如果我不想等待后台工作者,则可以致电.CancelAsync。但这不是这里的设计要求。
伊恩·博伊德

1
+1用于指出CancelAsync方法的值,这是在等待后台工作程序。
伊恩·博伊德

10

几乎所有人都对这个问题感到困惑,并且不了解如何使用工人。

考虑一个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();
}

现在,我可以这样编写代码,但是我不会。我不在乎,我只是不在乎。


18
WaitForWorkerToFinish方法在哪里?任何完整的源代码?
Kiquenet

4

您可以查看到RunWorkerCompletedEventArgsRunWorkerCompletedEventHandler看当时的状态。成功,已取消或错误。

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

2
要求是在工作程序完成之前,CancelDoingStuff()不能返回。检查它的完成情况确实没有意义。
伊恩·博伊德

然后,您将必须创建一个事件。确实,它与BackgroundWorker确实没有任何关系,您只需要实现一个事件,侦听它并在完成后触发即可。而RunWorkerCompletedEventHandler就是完成时。触发另一个事件。
Seb Nilsson

4

无需等待后台工作人员完成。这几乎违反了启动单独线程的目的。相反,您应该让您的方法完成,然后将依赖于完成的所有代码移动到其他位置。您让工作人员告诉您完成的时间,然后调用其余的代码。

如果要等待完成,请使用提供WaitHandle的其他线程构造。


1
你能推荐一个吗?后台工作者似乎是唯一可以将通知发送到构造BackgroundWorker对象的线程的线程构造。
伊恩·博伊德

后台工作人员是一个UI构造。它仅使用您自己的代码仍需要知道如何调用的事件。没有什么可以阻止您为此创建自己的代理。
Joel Coehoorn

3

您为什么不能只绑定到BackgroundWorker.RunWorkerCompleted事件。这是一个回调,将“在后台操作完成,已取消或引发异常时发生。”


1
因为使用DoesStuff对象的人要求其取消。对象使用的共享资源将要断开连接,删除,处置,关闭,并且它需要知道它已完成,这样我才能继续。
伊恩·博伊德

1

我不明白您为什么要等待BackgroundWorker完成;看来,这与上课动机完全相反。

但是,您可以通过调用worker.IsBusy来启动每个方法,并在运行时退出它们。


错误的单词选择;我不是在等它完成。
伊恩·博伊德

1

只是想说我来这里是因为我需要后台工作人员在循环中运行异步进程时等待,所以我的修复比其他所有事情都容易^^

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

只是想和我分享一下,因为这是我寻找解决方案时的结局。另外,这是我的第一篇关于堆栈溢出的文章,因此,如果它不好或我要批评家们满意的话!:)


0

嗯,也许我的意思没对。

后台工作人员的“ workermethod”(处理backgroundworker.doWork-event的方法/函数/子程序)完成后,将调用WorkerCompleted事件, 因此无需检查BW是否仍在运行。如果要停止工作,请检查“工作人员方法”中的取消待处理属性


我知道该工作人员必须监视CancellationPending属性并尽快退出。但是请求后台工作人员的外部人员如何取消,等待完成?
伊恩·博伊德

WorkCompleted事件仍将触发。
Joel Coehoorn

“但是,要求后台工作人员取消的外部人员如何等待它完成呢?”
伊恩·博伊德

您可以通过等待WorkCompleted事件触发来等待它完成。如果要防止用户在整个GUI上都单击鼠标,则可以使用@Joe在上面答案中提出的一种解决方案(禁用表格或显示模式)。换句话说,让系统的空闲循环来等待您。它会告诉您它何时结束(通过触发事件)。
杰夫2013年

0

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放到derived上BackgroundWorker,然后重写OnRunWorkerCompleted以设置该标记。但是,这仍然有点疯狂。我建议将cancel事件视为异步方法,并执行RunWorkerCompleted处理程序中当前正在执行的任何操作。


将代码移到RunWorkerCompleted并不属于它,也不是很漂亮。
伊恩·博伊德

0

我在这里参加聚会有点晚了(大约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 。如果您的意图确实是锁定并等待取消,那么这对您来说是没有用的,但是如果您只想等待以便可以启动另一个进程,那么此方法就很好了。


0

我知道这真的很晚(5年),但是您要寻找的是使用Thread和SynchronizationContext。您将不得不“手动”将UI调用封送回UI线程,而不是让Framework自动进行。

这使您可以使用需要时可以等待的线程。


0
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

你有什么问题?在SO中,您在问问题时必须明确
Alma Do

@这不是问题。
Code Lღver

0

弗雷德里克·卡尔塞斯(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();可能永远不会被调用!

_resetEvent.Set();即使在我们的DoWork处理程序中发生异常,try-finally 也会确保将调用该方法。否则,应用程序可能会在调用时永久冻结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类 (Microsoft文档)


0

关闭表单会关闭我打开的日志文件。我的后台工作人员写了该日志文件,因此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.)

0

您可以背负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;
    }
}

0

我使用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以集_isBusyfalse在异常。或者,只需_worker.IsBusyStopAsync while循环。

这是完整实施的示例:

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. 等待Task.Delay不正确。在我的PC上,Task.Delay(1)实际上等待约20毫秒。

-2

哦,伙计,其中有些变得异常复杂。您需要做的就是检查DoWork处理程序中的BackgroundWorker.CancellationPending属性。您可以随时检查。待处理后,设置e.Cancel = True并从方法中保释。

//这里的方法private void Worker_DoWork(object sender,DoWorkEventArgs e){BackgroundWorker bw =(sender as BackgroundWorker);

// do stuff

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

// do other stuff

}


1
使用此解决方案,叫CancelAsync的人如何被迫等待后台工作人员取消?
伊恩·博伊德
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.