C# 取消源错误行为

C# 取消源错误行为,c#,invoke,cancellationtokensource,C#,Invoke,Cancellationtokensource,我在使用CancellationTokenSource取消任务后等待任务时遇到问题。取消调用不会中断任务。当我等待的时候 对于任务,主线程会阻塞,因为任务永远不会中断 以下是对我的程序的简短描述: 任务增加一个char变量(从'A'到'Z'),并在GUI线程上显示它。为此,任务在创建控件的线程上执行委托(this.invoke()) 一旦我注释掉RefreshTextBox()函数,cancel调用就会工作,任务就会中断。似乎this.invoke()命令可以防止任务中断 在下面的代码中,我还使

我在使用CancellationTokenSource取消任务后等待任务时遇到问题。取消调用不会中断任务。当我等待的时候 对于任务,主线程会阻塞,因为任务永远不会中断

以下是对我的程序的简短描述: 任务增加一个char变量(从'A'到'Z'),并在GUI线程上显示它。为此,任务在创建控件的线程上执行委托(this.invoke())

一旦我注释掉RefreshTextBox()函数,cancel调用就会工作,任务就会中断。似乎this.invoke()命令可以防止任务中断

在下面的代码中,我还使用普通线程实现了相同的功能。然后我工作。任务实现和线程实现的区别在哪里

using System.Windows.Forms;
using System.Threading;
using System.Threading.Tasks;

public partial class frm_Main : Form
{
    private delegate void dgt_StringHandler(string str_Value);
    CancellationTokenSource _obj_Cts = null;
    Thread _obj_Thread = null;
    Task _obj_Task = null;

    public frm_Main()
    {
        InitializeComponent();
    }

    private void CreateChar(ref char chr_Value)
    {
        int int_Value;

        int_Value = (int)chr_Value;
        int_Value++;

        if (int_Value > 90 || int_Value < 65)
            int_Value = 65;

        chr_Value = (char)int_Value;
    }

    private void TestThread()
    {
        char chr_Value = '@';
        bool bol_Stop = false;

        while (!bol_Stop)
        {
            try
            {
                Thread.Sleep(300);
                CreateChar(ref chr_Value);
                RefreshTextBox(chr_Value.ToString());
            }
            catch (ThreadInterruptedException)
            {
                bol_Stop = true;
            }
        }
    }

    private void TestTask(object obj_TokenTmp)
    {
        char chr_Value = '@';
        CancellationToken obj_Token = (CancellationToken)obj_TokenTmp;

        while (!obj_Token.IsCancellationRequested)
        {
            Thread.Sleep(300);
            CreateChar(ref chr_Value);
            RefreshTextBox(chr_Value.ToString());
        }
    }

    private void RefreshTextBox(string str_Value)
    {
        if (txt_Value.InvokeRequired)
        {
            dgt_StringHandler obj_StringHandler = new dgt_StringHandler(RefreshTextBox);
            this.Invoke(obj_StringHandler, new object[] { str_Value });
        }
        else
        {
            txt_Value.Text = str_Value;
        }
    }

    private void btn_StartStop_Click(object sender, EventArgs e)
    {
        if (_obj_Task == null && _obj_Thread == null)
        {
            if (opt_Task.Checked)
            {
                _obj_Cts = new CancellationTokenSource();
                _obj_Task = new Task(new Action<object>(TestTask), _obj_Cts.Token, _obj_Cts.Token);
                _obj_Task.Start();
            }
            else
            {
                _obj_Thread = new Thread(new ThreadStart(TestThread));
                _obj_Thread.Start();
            }

            btn_StartStop.Text = "Stop";
        }
        else
        {
            if (_obj_Thread != null)
            {
                _obj_Thread.Interrupt();
                _obj_Thread.Join();
                _obj_Thread = null;
            }

            if (_obj_Task != null)
            {
                _obj_Cts.Cancel();
                _obj_Task.Wait();
                _obj_Task = null;
                _obj_Cts = null;
            }

            btn_StartStop.Text = "Start";
        }
    }
}
使用System.Windows.Forms;
使用系统线程;
使用System.Threading.Tasks;
公共部分类frm_Main:表单
{
私有委托无效dgt_StringHandler(字符串str_值);
CancellationTokenSource _obj_Cts=null;
线程_obj_Thread=null;
Task _obj_Task=null;
公共财政司司长()
{
初始化组件();
}
私有void CreateChar(ref char chr_值)
{
int_值;
int_值=(int)chr_值;
int_值++;
如果(int|U值>90 | int|U值<65)
int_值=65;
chr_值=(char)int_值;
}
私有void TestThread()
{
字符chr_值='@';
bool bol_Stop=false;
而(!bol_Stop)
{
尝试
{
睡眠(300);
CreateChar(参考chr_值);
RefreshTextBox(chr_Value.ToString());
}
捕获(ThreadInterruptedException)
{
bol_停止=真;
}
}
}
私有void测试任务(对象obj_TokenTmp)
{
字符chr_值='@';
取消令牌obj_令牌=(取消令牌)obj_令牌TMP;
而(!obj_Token.iscancellationrequest)
{
睡眠(300);
CreateChar(参考chr_值);
RefreshTextBox(chr_Value.ToString());
}
}
私有无效刷新文本框(字符串str_值)
{
如果(txt_值。调用需要)
{
dgt_StringHandler obj_StringHandler=新dgt_StringHandler(刷新文本框);
Invoke(obj_StringHandler,新对象[]{str_Value});
}
其他的
{
txt_Value.Text=str_值;
}
}
私有无效btn\u开始停止单击(对象发送者,事件参数e)
{
if(_obj_Task==null&&u obj_Thread==null)
{
如果(选择任务。选中)
{
_obj_Cts=新的CancellationTokenSource();
_obj_任务=新任务(新操作(TestTask),_obj_Cts.Token,_obj_Cts.Token);
_obj_Task.Start();
}
其他的
{
_obj_线程=新线程(新线程开始(测试线程));
_obj_Thread.Start();
}
btn_StartStop.Text=“停止”;
}
其他的
{
如果(_obj_Thread!=null)
{
_obj_线程中断();
_obj_螺纹连接();
_obj_线程=空;
}
如果(_obj_Task!=null)
{
_obj_Cts.Cancel();
_obj_任务。等待();
_obj_Task=null;
_obj_Cts=null;
}
btn_StartStop.Text=“开始”;
}
}
}

这两段代码一起构成死锁:

_obj_Cts.Cancel();
_obj_Task.Wait();

您正在主线程上调用
Wait()
,Invoke()需要由主线程处理

您可以使用
this.BeginInvoke(…)
来打破死锁


线程版本使用中断,一个大锤。因此,线程不会在停止信号后尝试调用
refreshttextbox()

这两段代码一起构成死锁:

_obj_Cts.Cancel();
_obj_Task.Wait();

您正在主线程上调用
Wait()
,Invoke()需要由主线程处理

您可以使用
this.BeginInvoke(…)
来打破死锁


线程版本使用中断,一个大锤。因此,线程不会在停止信号后尝试调用
refreshttextbox()

这是经过调整的代码。我现在调用BeginInvoke()而不是Henk Holterman建议的Invoke()。这非常有效,是防止死锁的唯一正确方法。还有一种情况必须考虑。我还有一个通过BeginInvoke()调用提供的IAsyncResult对象。这个对象用于检查异步调用是否已经完成。如果我不检查它,可能是GUI线程的速度不够快(例如GUI线程中的某个sleep语句),无法执行我的委托,因此,尽管GUI线程尚未完成最后一次委托,但我的TestTask()方法始终会调用BeginInvoke()。结果是我的GUI线程会阻塞应用程序

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;
    using System.Threading.Tasks;

    namespace InvokeTest
    {
    public partial class frm_Main : Form
    {

    private delegate void dgt_StringHandler(string str_Value);
    CancellationTokenSource _obj_Cts = null;
    Thread _obj_Thread = null;
    Task _obj_Task = null;
    IAsyncResult _obj_Ar = null;

    public frm_Main()
    {
        InitializeComponent();
    }

    private void CreateChar(ref char chr_Value)
    {
        int int_Value;

        int_Value = (int)chr_Value;
        int_Value++;

        if (int_Value > 90 || int_Value < 65)
            int_Value = 65;

        chr_Value = (char)int_Value;
    }


    private void TestThread()
    {
        char chr_Value = '@';
        bool bol_Stop = false;

        while (!bol_Stop)
        {
            try
            {
                Thread.Sleep(1); // is needed for interrupting the thread
                CreateChar(ref chr_Value);
                RefreshTextBox(chr_Value.ToString());
            }
            catch (ThreadInterruptedException)
            {
                bol_Stop = true;
            }
        }
    }

    private void TestTask(object obj_TokenTmp)
    {
        char chr_Value = '@';
        CancellationToken obj_Token = (CancellationToken)obj_TokenTmp;

        while (!obj_Token.IsCancellationRequested)
        {
            CreateChar(ref chr_Value);
            RefreshTextBox(chr_Value.ToString());
        }
    }


    private void RefreshTextBox(string str_Value)
    {            
        if (txt_Value.InvokeRequired)
        {
            if (_obj_Ar == null ||
                _obj_Ar.IsCompleted)
            {
                dgt_StringHandler obj_StringHandler = new dgt_StringHandler(RefreshTextBox);
                _obj_Ar = this.BeginInvoke(obj_StringHandler, new object[] { str_Value });
            }
        }
        else
        {
            Thread.Sleep(200);
            txt_Value.Text = str_Value;
        }
    }


    private void btn_StartStop_Click(object sender, EventArgs e)
    {
        if (_obj_Task == null && _obj_Thread == null)
        {
            if (opt_Task.Checked)
            {
                _obj_Cts = new CancellationTokenSource();
                _obj_Task = new Task(new Action<object>(TestTask), _obj_Cts.Token, _obj_Cts.Token);
                _obj_Task.Start();
            }
            else
            {
                _obj_Thread = new Thread(new ThreadStart(TestThread));
                _obj_Thread.Start();
            }

            btn_StartStop.Text = "Stop";
        }
        else
        {
            if (_obj_Thread != null)
            {
                _obj_Thread.Interrupt();
                _obj_Thread.Join();
                _obj_Thread = null;
            }

            if (_obj_Task != null)
            {
                _obj_Cts.Cancel();
                _obj_Task.Wait();
                _obj_Task = null;
                _obj_Cts = null;
            }

            btn_StartStop.Text = "Start";
        }
    }

    private void frm_Main_FormClosing(object sender, FormClosingEventArgs e)
    {
        if (_obj_Thread != null)
        {
            _obj_Thread.Interrupt();
            _obj_Thread.Join();
        }

        if (_obj_Task != null)
        {
            _obj_Cts.Cancel();
            _obj_Task.Wait();
        }
    }

    }
    }
使用系统;
使用System.Collections.Generic;
使用系统组件模型;
使用系统数据;
使用系统图;
使用System.Linq;
使用系统文本;
使用System.Windows.Forms;
使用系统线程;
使用System.Threading.Tasks;
名称空间调用测试
{
公共部分类frm_Main:表单
{
私有委托无效dgt_StringHandler(字符串str_值);
CancellationTokenSource _obj_Cts=null;
线程_obj_Thread=null;
Task _obj_Task=null;
IAsyncResult_obj_Ar=null;
公共财政司司长()
{
初始化组件();
}
私有void CreateChar(ref char chr_值)
{
int_值;