我的WinForm应用程序中的背景工作人员遇到了问题。 这是我的情况: 我有一个以OnLoad表单事件开始的后台工作人员。然后,我在表单上有一个复选框来停止/启动工作人员。 当我取消选中该框时,事件将调用cancelAsync()方法,但工作程序不会收到CancellationPending。 为了调试此问题,我尝试在窗体上添加一个与CheckedChanged事件具有相同功能的按钮,在这种情况下,它可以正常工作!
这是我的代码的片段:
工人...
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();
}
当我单击按钮时,工作人员会收到取消信号并退出设置bwMBExitEvent(ManualResetEvent)的循环。这样,单击事件WaitOne结束等待。 当我取消选中工作程序框时,停止运行,但不接收信号,因此不要结束循环并且未设置事件。 CheckedChanged的WaitOne永远不会结束。
请原谅任何英语语法或拼写问题。
答案 0 :(得分:1)
首先,BGW已过时,已完全由async/await
,Tasks和Progress<T>
取代。任务允许合成,连续和取消,这对于BGW而言相当复杂。我怀疑BGW完成后,bwMBExitEvent
事件用于实现 continuation 。
文章Async in 4.5: Enabling Progress and Cancellation in Async APIs解释了.NET 4.5及更高版本(即所有受支持的版本)中取消和进度报告的工作方式。
也就是说,BGW的取消没有问题。我怀疑这个事件,loop
变量和其他未知代码最终导致竞争状况。
使用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个任务,并将它们存储在一个数组中。它还创建一个 new CancellationTokenSource,可用于向所有任务或线程发出取消通知信号,并监视其令牌
要通过按钮调用CancellationTokenSource.Cancel()取消任务,并等待所有任务完成:
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
,因为在await
之后,UI线程恢复了执行。
所有worker方法要做的就是检查CancellationToken.IsCancellationRequested标志:
private void WorkerMethod1(CancellationToken token)
{
//If cancellation isn't requested
while(!token.IsCancellationRequested)
{
//Loop one more time
}
}
将所有内容放在一起:
//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;
}