后台工作者不会在 c# WinForm 应用程序中收到 CancellationPending
Background Worker won't receive CancellationPending in c# WinForm application
我的 WinForm 应用程序中的 backgroundworkers 有问题。
这是我的场景:
我有一个在 OnLoad 表单事件中启动的后台工作人员。然后我在表格上有一个复选框 stop/start 工人。
当我取消选中该框时,事件调用 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 永远不会结束。
请原谅任何英语语法或拼写问题。
首先,BGW 已经过时,完全被 async/await
、Tasks 和 Progress<T>
取代。任务允许组合、延续和取消,这对于 BGW 来说是相当复杂的。我怀疑 bwMBExitEvent
事件用于在 BGW 完成后实现 continuation。
文章 Async in 4.5: Enabling Progress and Cancellation in Async APIs 解释了取消和进度报告在 .NET 4.5 及更高版本(即所有支持的版本)中的工作原理。
也就是说,BGW 取消没有问题。我怀疑该事件,loop
变量和其他非种子代码最终导致竞争条件。
虽然使用 2、4 或 10 个可取消任务而不是 BGW 很容易。
- 使用 Task.Run 可以轻松启动多个任务。
- 可以等待多个任务完成而不会阻塞 Task.WhenAll。
- 可以通过 CancellationTokenSource
向线程、异步操作任务发出取消信号
启动多个任务很容易:
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;
}
我的 WinForm 应用程序中的 backgroundworkers 有问题。 这是我的场景: 我有一个在 OnLoad 表单事件中启动的后台工作人员。然后我在表格上有一个复选框 stop/start 工人。 当我取消选中该框时,事件调用 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 永远不会结束。
请原谅任何英语语法或拼写问题。
首先,BGW 已经过时,完全被 async/await
、Tasks 和 Progress<T>
取代。任务允许组合、延续和取消,这对于 BGW 来说是相当复杂的。我怀疑 bwMBExitEvent
事件用于在 BGW 完成后实现 continuation。
文章 Async in 4.5: Enabling Progress and Cancellation in Async APIs 解释了取消和进度报告在 .NET 4.5 及更高版本(即所有支持的版本)中的工作原理。
也就是说,BGW 取消没有问题。我怀疑该事件,loop
变量和其他非种子代码最终导致竞争条件。
虽然使用 2、4 或 10 个可取消任务而不是 BGW 很容易。
- 使用 Task.Run 可以轻松启动多个任务。
- 可以等待多个任务完成而不会阻塞 Task.WhenAll。
- 可以通过 CancellationTokenSource 向线程、异步操作任务发出取消信号
启动多个任务很容易:
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
.
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;
}