停止BackgroundWorker启动的线程

3

我有一个使用了BackgroundWorker的Windows表单。BackgroundWorker实例化了一个对象,然后执行该对象中的方法。我的问题是,当我使用backgroundworker.CancelAsync时,运行在远程对象上的方法并没有停止。在下面的示例中,dowork方法在单击取消按钮后继续执行。FYI,dowork正在遍历电子表格,并根据电子表格中的行进行一些数据操作。

    private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        myObject newObject = new myObject();
        newObject.dowork();

        if (backgroundWorker1.CancellationPending)
        {
            e.Cancel = true;
            backgroundWorker1.ReportProgress(0);
            return;
        }
    }

     private void btnCancel_Click(object sender, EventArgs e)
    {
       if (backgroundWorker1.IsBusy) backgroundWorker1.CancelAsync();
    }

你的想法是什么?

谢谢


2
新对象.newObject.dowork()会检查backgroundWorker1.CancellationPending吗?而myObject newObject = new myObject();又是一个新线程吗? - paparazzo
4
我确定newObject.dowork()是阻塞的,所以后台工作线程永远不会评估取消语句。你可以创建一个任务来代替使用后台工作线程,但取消同步方法可能很困难... - Ron Beyer
正如Blam所暗示的那样(读懂其中的含义),将BackgroundWorker的引用传递到你的myObject实例中(最好通过构造函数),并在循环内部检查CancellationPending标志。 - Idle_Mind
1个回答

2
btnCancel_Click中,您必须将取消请求传递给您的工作对象;否则,它将永远不会被通知。 BackgroundWorker.CancelAsync()仅简单地设置了BackgroundWorker.CancellationPending属性,通知BackgroundWorker的使用者(UI而非执行任务的人)您的任务已被取消。

因此,您需要:

MyObject myObject;

// This method is executed on the worker thread. Do not access your controls
// in the main thread from here directly.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
    myObject = new MyObject();

    // The following line is a blocking operation in this thread.
    // The user acts in the UI thread, not here, so you cannot do here
    // anything but wait.
    myObject.DoWork();

    // Now DoWork is finished. Next line is needed only to notify
    // the caller of the event whether a cancel has happened.
    if (backgroundWorker1.CancellationPending)
        e.Cancel = true;

    myObject = null;
}

private void btnCancel_Click(object sender, EventArgs e)
{
   if (backgroundWorker1.IsBusy)
   {
       backgroundWorker1.CancelAsync();

       // You must notify your worker object as well.
       // Note: Now you access the worker object from the main thread!
       // Note2: It would be possible to pass the worker to your object
       //        and poll the backgroundWorker1.CancellationPending from there,
       //        but that would be a nasty pattern. BL objects should not
       //        aware of the UI components.
       myObject.CancelWork();
   }
}

那么你应该如何实现通知:

public class MyObject
{
    // normally you should use locks to access fields from different threads
    // but if you just set a bool from one thread and read it from another,
    // then it is enough to use a volatile field.
    private volatile bool isCancelRequested;

    // this will be called from the main thread
    public void CancelWork()
    {
        isCancelRequested = true;
    }

    // This method is called from the worker thread.
    public void DoWork()
    {
        // Make sure you poll the isCancelRequested field often enough to
        // react to the cancellation as soon as possible.
        while (!isCancelRequested && ...)
        {
            // ...
        }
    }
}

这个注释“确保你经常轮询isCancelRequested字段,以便尽快对取消操作做出反应”并不是微不足道的。 - Conrad Frix
@Conrad Frix:当然可以。:)特别是当你需要从深层方法调用堆栈中返回时。但我不建议仅仅为了自己而抛出异常或调用Thread.Abort。它们看起来很方便,但非常不干净。实际上,我也不建议使用BackgroundWorker,因为TPL已经可用了... - György Kőszeg

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