CancellationTokenSource行为不端

时间:2011-12-02 12:46:30

标签: c# invoke cancellationtokensource

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

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

一旦我注释掉RefreshTextBox() - 函数,取消调用就会起作用,任务就会被中断。似乎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";
        }
    }
}

2 个答案:

答案 0 :(得分:1)

这两段代码一起形成了僵局:

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

this.Invoke(obj_StringHandler, new object[] { str_Value });

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

您可以使用this.BeginInvoke(...)来解决僵局。

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

答案 1 :(得分:0)

这是改编的代码。我现在调用BeginInvoke()而不是Henk Holterman提出的Invoke()。这非常好,是防止死锁的唯一正确方法。还有另一个案例必须加以考虑。我还有一个通过BeginInvoke()调用给出的IAsyncResult对象。我用这个对象来检查异步调用是否已经完成。如果我不检查它可能是GUI线程不够快(例如在GUI线程中的某个地方的睡眠语句)来执行我的委托和我的TestTask()方法的原因总是会调用BeginInvoke()虽然GUI线程尚未完成最后一个委托。结果是我的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();
        }
    }

    }
    }