BackgroundWorker - RunWorkerCompleted 中的 CancellationPending 为什么会变成 false?

5
取消后台工作程序时,在DoWork方法中,CancellationPending属性为true,但在RunWorkerCompleted方法中,CancellationPending属性为false。我不知道我做错了什么?
static BackgroundWorker b1;

static void Main(string[] args)
{
    b1=new BackgroundWorker();
    b1.DoWork += new DoWorkEventHandler(work1);
    b1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(completed);
    b1.WorkerSupportsCancellation = true;
    b1.RunWorkerAsync("Hellow");
    Console.ReadLine();
}

private static void completed(object sender, RunWorkerCompletedEventArgs e)
{
    if (((BackgroundWorker)sender).CancellationPending)
        Console.WriteLine("Canceled!");
    else
        Console.WriteLine("Result:" + e.Result);//it goes here every time
}

private static void work1(object sender, DoWorkEventArgs e)
{
    ((BackgroundWorker)sender).CancelAsync();
    if (((BackgroundWorker)sender).CancellationPending)
    {
        e.Cancel = true;
    }
}

顺便问一下,我如何将DoWork中发生的错误添加到RunWorkerCompletedEventArgs.Error中,以便向用户显示?

2个回答

8
是的,BackgroundWorker类在引发RunWorkerCompleted事件之前将CancellationPending属性设置为false。无论工作线程是否实际被取消。
这是有意为之的,它可以避免你在使用线程时经常遇到的一种称为“竞争条件”的严重问题。由于“竞争条件”而导致的线程问题很常见,也很难调试。
如果BGW没有这样做,你计划中的方法容易出错,因为当你看到CancellationPending属性设置为true时,你会认为工作线程已被取消。但这只是一种幻觉,你无法区分它是被取消了还是正常完成了。一个特殊情况是:在工作线程完成前的微秒级别内调用了CancelAsync()方法。工作线程甚至没有机会看到CancellationPending标志被设置为true,因为它正在完成DoWork事件处理程序方法的最后一些细节。这就是“竞争条件”,工作线程超过了你的调用并正常地完成了。
避免这个bug的正确方式是,当工作线程看到CancellationPending属性被设置为true时,它应该将e.Cancel设置为true,并停止正在进行的操作。现在它是可靠的,RunWorkerCompleted事件处理程序中的e.Cancelled属性是e.Cancel的副本。所以你的代码现在可以可靠地告诉你工作线程是否看到了取消请求。

如果您在程序中传递BackgroundWorker对象,那么您还必须将DoWorkEventArgs与其一起传递以执行Hans所建议的操作。我通常将BackgroundWorker命名为worker,将DoWorkEventArgs命名为dwea - 然后我可能会有一个上下文,在其中执行以下操作:`if(worker.CancellationPending){dwea.Cancel = true; return;}` - Jesse Adam

6
我认为CancellationPending属性是在后台操作(即您的work1方法)期间使用的。它会告诉后台工作者,您已请求取消后台操作。一旦调用了RunWorkerCompleted事件,后台工作者已经完成取消请求的工作,因此取消将不再挂起。
编辑:RunWorkerCompletedEventArgs有一个Cancelled属性,它将告诉您后台操作是否已被取消。
如果您从DoWork方法(在您的情况下为work1)中抛出异常,它应该被BackgroundWorker捕获,并将其填充到RunWorkerCompletedEventArgs的Error属性中。

在每个在线示例中,他们都使用CancellationPending来在RunWorkerCompleted中检查是否取消了bgw。感谢您的时间,但是您是错误的。 - Stav Alfi
3
实际上,@brian s是正确的。您需要在completed函数中检查RunWorkerCompletedEventArgs参数中的Cancelled标志。 http://msdn.microsoft.com/en-us/library/system.componentmodel.runworkercompletedeventargs.aspx - Surfbutler
1
是的,谢谢@Surfbutler,我忘记了Cancelled标志。 - Brian S
1
哦!我明白了,谢谢大家,对不起 Brain S。再次感谢你们。 - Stav Alfi
没关系,如果我记得取消标志,就不会那么困惑了。很高兴你解决了它。我会编辑答案,以免将来让其他人感到困惑。 - Brian S

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接