CancellationTokenSource неправильное поведение - PullRequest
1 голос
/ 02 декабря 2011

У меня проблема, когда я жду задачу после того, как отменил ее с помощью 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 ]

1 голос
/ 02 декабря 2011

Эти 2 части кода вместе образуют тупик:

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

и

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

Вы вызываете Wait() в главном потоке, и Invoke () долженбыть обработанным главным потоком.

Вы можете выйти из тупика, используя вместо этого this.BeginInvoke(...).

В версии Thread используется кувалда Interrupt.Таким образом, поток не будет пытаться вызвать RefreshTextBox() после сигнала остановки.

0 голосов
/ 05 декабря 2011

Вот адаптированный код. Теперь я вызываю BeginInvoke () вместо Invoke (), как предложил Хенк Холтерман. Это работает очень хорошо и является единственным правильным способом предотвращения тупика. Есть еще один случай, который необходимо рассмотреть. У меня также есть объект IAsyncResult, который передается через вызов BeginInvoke (). Этот объект я использую, чтобы проверить, завершился ли асинхронный вызов или нет. Если бы я не проверял это, возможно, что поток GUI недостаточно быстр (например, оператор сна где-то в потоке 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();
        }
    }

    }
    }
...