4

取消 BackGroundWorker 后,在 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 以将其发送给用户?

4

2 回答 2

8

是的,BackgroundWorker 类在引发 RunWorkerCompleted 事件之前将 CancellationPending 属性设置为 false。工人是否实际被取消。

这是有意为之的,它可以防止您掉入使用线程时始终存在的令人讨厌的陷阱。由于一种称为“线程竞争”的错误,使用线程的代码通常会随机且不可预测地出现错误行为。这是一种非常常见的错误,并且非常难以调试。

如果 BGW 不这样做,那么在您的预期方法中很容易出错的是,当您看到 CancellationPending 设置为 true 时,您会假设工作人员已被取消。但这是一种错觉,你无法分辨它被取消和正常完成之间的区别。极端情况是您在工作人员完成前一微秒调用 CancelAsync()。工作人员甚至没有机会看到 CancellationPending 标志设置为 true,它正忙于完成 DoWork 事件处理程序方法的最后一位。这是一场线程竞赛,工作人员在您的呼叫之前比赛并正常完成。

避免此错误的正确握手是您的工作人员在看到 CancellationPending 属性设置为 true 时将 e.Cancel 设置为 true。当然,停止它正在做的事情。现在可以确定了,RunWorkerCompleted 事件处理程序中的 e.Cancelled 属性是 e.Cancel 的副本。因此,您的代码现在可以可靠地告诉您工作人员是否看到了取消请求。

于 2012-07-29T15:59:55.630 回答
6

我相信 CancellationPending 属性是在后台操作期间使用的(在您的 work1 方法中)。它将告诉后台工作人员您已请求取消后台操作。一旦调用了 RunWorkerCompleted 事件,后台工作人员已经完成了取消请求的工作,因此取消不再处于挂起状态。

编辑: RunWorkerCompletedEventArgs 有一个 Canceled 属性,它会告诉你后台操作是否被取消。

如果您从 DoWork 方法(在您的情况下为 work1)抛出异常,它应该被 BackgroundWorker 捕获并填充 RunWorkerCompletedEventArgs 的 Error 属性。

于 2012-07-29T14:33:15.243 回答