2011-12-02 28 views
1

我在使用CancellationTokenSource取消它後等待任務時出現問題。取消呼叫不會中斷任務。當我爲任務等待 時,主線程會阻塞,因爲任務不會中斷。CancellationTokenSource misbehavior

下面是我的程序的簡短說明: 任務將字符變量(從'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"; 
     } 
    } 
} 

回答

1

這2個代碼一起形成僵局:

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

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

你在主線程調用Wait(),和invoke()需要由主線程處理。

您可以改爲使用this.BeginInvoke(...)來打破死鎖。

線程版本使用中斷,一個大錘。所以線程在停止信號後不會嘗試呼叫RefreshTextBox()

+0

謝謝您的回答。但這不是解決問題的辦法。我想知道爲什麼我無法使用CancellationTokenSource取消它。當我註釋掉Wait語句時,任務仍在後臺運行(或者在我的情況下阻塞)並且永遠不會結束。 –

+0

我不清楚爲什麼當一個調用Interrupt()和Join()時,相同的例程與普通線程一起工作。當我用一個任務實現它並調用Cancel()然後Wait()時它不起作用。區別在哪裏。當我註釋掉refreshbox()方法時,Cancel()和Wait()也適用於任務實現! –

+0

「任務仍在運行」 - 那就是你的問題。取消是一項合作行爲。你的任務應該停止並停止。 Thread.Interrupt(或.Abort)功能強大但不安全。 –

0

這裏是改編的代碼。我現在稱Henk Holterman提出的BeginInvoke()而不是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(); 
     } 
    } 

    } 
    } 
相關問題