背景工人赢得';无法在c#WinForm应用程序中接收取消挂起

背景工人赢得';无法在c#WinForm应用程序中接收取消挂起,c#,winforms,checkbox,backgroundworker,C#,Winforms,Checkbox,Backgroundworker,我的WinForm应用程序中的后台工作人员有问题。 以下是我的设想: 我有一个以OnLoad表单事件开始的后台工作程序。然后,我在表单上有一个复选框,用于停止/启动工人。 当我取消选中该框时,事件将调用cancelAsync()方法,但工作进程不会收到CancellationPending。 为了调试这个问题,我尝试在表单上添加一个按钮,该按钮执行与CheckedChanged事件相同的操作,在这种情况下,它可以工作 这是我的代码片段: 工人们 private void BwMB_DoWork(

我的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();
}
当我点击按钮时,工人接收取消信号并退出循环设置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;
}