使用 BackgroundWorker 冻结表单

Form freezing using BackgroundWorker

我正在使用 WinForm,我需要的所有进程都可以从中控制。现在我正在尝试将 BackgroundWorkerProgressBar 和取消按钮集成到我的代码中。我希望它在本地围绕我的代码而不是在单独的方法中。为了对此进行测试,创建了一个带有进度条(尚未激活)和用于停止 for 循环的按钮的新表单。但是,代码不起作用(甚至还没有包含进度条)。表单立即冻结(见图),所以我无法测试取消按钮。然而,执行了 for 循环并显示了 "Done: " + l.ToString()。我该如何解决这个问题?

void stopMeasurement(object sender, EventArgs e) 
{
    stopMeas = true;    
}

public void testcancel() // Test method which is triggered manually
{
    int l = 0;

    MetingProgress metingProgress = new MetingProgress();
    metingProgress.btnCancelmeting.Click += new EventHandler(stopMeasurement);

    BackgroundWorker worker = new BackgroundWorker();
    worker.WorkerSupportsCancellation = true;
    worker.DoWork += (sender, args) =>
    {                       
        for (int k = 0; k < 10; k++)
        {
            Thread.Sleep(1000);
            l++;

            if (worker.CancellationPending)
                break;
        }

        MessageBox.Show("Done: " + l.ToString());

    };
    worker.RunWorkerAsync(); 

    while (worker.IsBusy) 
    {
        if (stopMeas)
            worker.CancelAsync();
    }

    metingProgress.Dispose();
    MessageBox.Show("All done");

}

这段代码是你的问题:

while (worker.IsBusy) 
{
    if (stopMeas)
        worker.CancelAsync();
}

您的 GUI 线程处于该循环中,直到您的工作人员完成。 您需要使您的工作者实例可以从 EventHandler 中访问并从那里调用 worker.CancelAsync()。


除此之外,我个人会分两步改进代码:

  1. 将整个 BackgroundWorker 移动到 MetingProgress class 并使其构造函数接受实际工作实现的委托。

  2. 使用 TAP(任务异步模式),即 async/await 有进度和 CancellationToken 的任务。

The form freezes immediately

这是因为您在主线程上有一个 while 循环 仍然 运行!所以表单不会响应。这称为忙等待。您将无法调用 CancelAsync 方法。

一个解决方案可能是删除 while 循环并将取消调用放入按钮事件代码中:

void stopMeasurement(object sender, EventArgs e)
{
    stopMeas = true;
    worker.CancelAsync();

}

您基本上完成的是:您创建了第二个取消令牌。所以另一种可能性是只使用 stopMeas 来取消后台操作:

worker.DoWork += (sender, args) =>
{                       
    for (int k = 0; k < 10; k++)
    {
        Thread.Sleep(1000);
        l++;

        if (stopMeas)
            break;
    }

    string mes = stopMeas ? "Done: " + l.ToString() : "Task aborted!";
    MessageBox.Show(mes);

};

编辑:还有这一行:

metingProgress.Dispose();

可能会导致 ObjectDisposed 异常。如果后台进程仍然 运行 并尝试更新您的进度条并且您已经处理了表单。您应该删除此行并将其留给垃圾收集器。