使用 BackgroundWorker 冻结表单
Form freezing using BackgroundWorker
我正在使用 WinForm,我需要的所有进程都可以从中控制。现在我正在尝试将 BackgroundWorker
与 ProgressBar
和取消按钮集成到我的代码中。我希望它在本地围绕我的代码而不是在单独的方法中。为了对此进行测试,创建了一个带有进度条(尚未激活)和用于停止 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()。
除此之外,我个人会分两步改进代码:
将整个 BackgroundWorker 移动到 MetingProgress class 并使其构造函数接受实际工作实现的委托。
使用 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 异常。如果后台进程仍然 运行 并尝试更新您的进度条并且您已经处理了表单。您应该删除此行并将其留给垃圾收集器。
我正在使用 WinForm,我需要的所有进程都可以从中控制。现在我正在尝试将 BackgroundWorker
与 ProgressBar
和取消按钮集成到我的代码中。我希望它在本地围绕我的代码而不是在单独的方法中。为了对此进行测试,创建了一个带有进度条(尚未激活)和用于停止 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()。
除此之外,我个人会分两步改进代码:
将整个 BackgroundWorker 移动到 MetingProgress class 并使其构造函数接受实际工作实现的委托。
使用 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 异常。如果后台进程仍然 运行 并尝试更新您的进度条并且您已经处理了表单。您应该删除此行并将其留给垃圾收集器。