2011-12-02 4 views
1

У меня возникли проблемы, когда я жду задания после того, как я отменил его с помощью CancellationTokenSource. Вызов отмены не прерывает задачу. Когда я жду для задачи, основная часть потока блокируется, потому что задача никогда не прерывается.CancellationTokenSource misbehavior

Краткое описание моей программы: Задача увеличивает переменную char (от 'A' до 'Z') и показывает ее в потоке графического интерфейса. Для этого задача выполняет делегат (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"; 
     } 
    } 
} 

ответ

1

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

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

и

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

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

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

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

+0

Благодарим вас за ответ. Но это не решение проблемы. Я хочу знать, почему я не могу отменить его с помощью CancellationTokenSource. Когда я комментирую оператор Wait, задача все еще выполняется (или в моем случае блокировка) в фоновом режиме и никогда не подходит к концу. –

+0

Мне непонятно, почему одни и те же подпрограммы работают с обычным потоком при вызове Interrupt(), а затем Join(). И когда я реализую его с заданием и вызываю Cancel(), а затем Wait(), он не работает. Где разница. Когда я комментирую метод refreshbox(), функции Cancel() и Wait() также работают с реализацией задачи! –

+0

«задача все еще запущена» - тогда это ваша проблема. Отмена - это совместное действие. Ваша задача должна прекратиться и воздержаться. Thread.Interrupt (или .Abort) является мощным, но небезопасным. –

0

Вот адаптированный код. Теперь я вызываю 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(); 
     } 
    } 

    } 
    } 

 Смежные вопросы

  • Нет связанных вопросов^_^