背景工人赢得';无法在c#WinForm应用程序中接收取消挂起
我的WinForm应用程序中的后台工作人员有问题。 以下是我的设想: 我有一个以OnLoad表单事件开始的后台工作程序。然后,我在表单上有一个复选框,用于停止/启动工人。 当我取消选中该框时,事件将调用cancelAsync()方法,但工作进程不会收到CancellationPending。 为了调试这个问题,我尝试在表单上添加一个按钮,该按钮执行与CheckedChanged事件相同的操作,在这种情况下,它可以工作 这是我的代码片段: 工人们背景工人赢得';无法在c#WinForm应用程序中接收取消挂起,c#,winforms,checkbox,backgroundworker,C#,Winforms,Checkbox,Backgroundworker,我的WinForm应用程序中的后台工作人员有问题。 以下是我的设想: 我有一个以OnLoad表单事件开始的后台工作程序。然后,我在表单上有一个复选框,用于停止/启动工人。 当我取消选中该框时,事件将调用cancelAsync()方法,但工作进程不会收到CancellationPending。 为了调试这个问题,我尝试在表单上添加一个按钮,该按钮执行与CheckedChanged事件相同的操作,在这种情况下,它可以工作 这是我的代码片段: 工人们 private void BwMB_DoWork(
private void BwMB_DoWork(object sender, DoWorkEventArgs e)
{
bwMBExitEvent.Reset();
bool loop = true;
while (loop)
{
if (bwMB.CancellationPending)
{
loop = false;
}
... other code ...
}
e.Cancel = true;
bwMBExitEvent.Set();
}
CheckedChanged事件
private void checkBoxModBus_CheckedChanged(object sender, EventArgs e)
{
try
{
if (checkBoxModBus.Checked)
{
if (!bwMB.IsBusy)
bwMB.RunWorkerAsync();
}
else
{
if (bwMB.IsBusy)
{
bwMB.CancelAsync();
bwMBExitEvent.WaitOne();
}
}
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
然后单击调试事件的按钮
private void button2_Click(object sender, EventArgs e)
{
bwMB.CancelAsync();
bwMBExitEvent.WaitOne();
}
当我点击按钮时,工人接收取消信号并退出循环设置BWMBEXIT事件(ManualResetEvent)。通过这种方式,click事件等待一端等待。
当我取消选中worker框时,停止运行,但不接收信号,因此不结束循环,事件未设置。支票的等待永远不会结束
请原谅任何英语语法或拼写问题。首先,BGW已经过时,完全被
async/wait
、Tasks和Progress
取代。任务允许合成、延续和取消,这对于BGWs来说相当复杂。我怀疑bwMBExitEvent
事件用于在BGW完成后实现延续
本文解释了.NET4.5及更高版本(即所有受支持的版本)中取消和进度报告的工作原理
也就是说,BGW对取消没有问题。我挂起事件,循环
变量和其他未查看的代码最终导致争用条件
使用2个、4个或10个可取消的任务代替BGW是很容易的
- 可以轻松启动多个任务李>
- 可以等待多个任务完成,而不使用阻塞李>
- 取消可以用信号通知线程、异步操作上的任务和
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
这段代码创建N个任务并将它们存储在一个数组中。它还创建了一个新的CancellationTokenSource,可用于向监视其令牌的所有任务或线程发送取消信号
要通过按钮调用取消任务并等待所有任务完成,请执行以下操作:
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
lblStatus.Text = "Cancelling";
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
lblStatus.Text = "Cancelled";
}
//Disable the button
Cancel.Enabled=false;
}
通过使用async/await
处理程序在等待任务完成时不会阻塞。它也不需要Invoke
或BeginInvoke
,因为执行在wait
之后在UI线程上恢复
所有辅助方法都必须检查标志:
把所有东西放在一起:
//Hold active tasks
Task[] _tasks;
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
CancellationTokenSource _cts;
private void OnLoad(...)
{
//Fire the tasks
StartTasks();
}
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
}
//Disable the button
Cancel.Enabled=false;
}
您不需要BGW的活动。首先,您不需要BGW,它是一个由Task、
async/await
和Progress
I second Panagiotis完全替代的遗留组件。请参阅:如果您有多个BGW,您确定正在监视正确的BGW吗?检查文档中的示例:它将发送方
强制转换到BGW,以确保其与正确的发送方一起工作删除所有“修复”BGW的尝试,并尝试使用干净的实现-无循环
,无bwMBExitEvent
,无e.Cancel=true代码>。如果要退出DoWork
,If(bwMB.CancellationPending){return;}
就足够了。但是,您必须明确取消每个BGW,并且不能等待它们完成。另一方面,对于任务来说,WaitOne()调用是一个等待发生的死锁,这很简单。永远不要阻塞UI线程。使用Debug>Windows>Threads来调试死锁,您可能会看到工作线程挂起调用。当UI线程紧张时无法完成。嗨,Panagiotis,谢谢你的YOUR回放,谢谢你。下周,在办公室,我将尝试根据您的建议更改我的BackgroundWorker代码。然后我会把结果写在这篇文章上。利奥纳多。
//Hold active tasks
Task[] _tasks;
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
CancellationTokenSource _cts;
private void OnLoad(...)
{
//Fire the tasks
StartTasks();
}
private void StartTasks()
{
_cts=new CancellationTokenSource();
//Start each method passing a CancellationToken
_tasks=new[]{
Task.Run(()=>WorkerMethod1(_cts.Token)),
Task.Run(()=>WorkerMethod2(_cts.Token)),
...
};
//Enable the Cancel button
Cancel.Enabled=true;
}
private async void Cancel_Clicked(object sender,EventArgs args)
{
if (_cts!=null)
{
//Signal a cancellation
_cts.Cancel();
//Asynchronously wait for all tasks to finish
await Task.WhenAll(_tasks);
_cts=null;
}
//Disable the button
Cancel.Enabled=false;
}