1. 程式人生 > >Thread(執行緒)四

Thread(執行緒)四

今天繼續講講執行緒的異常處理、執行緒取消、多執行緒的臨時變數和執行緒安全lock的問題。

1、非同步處理。

一般來說如果是同步方法的非同步處理,我們大多都是try catch住,但是非同步方法應該怎麼做呢。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #region 異常處理                //多執行緒的委託是不允許異常的, try catch包住,寫下日誌             
for  ( int  i = 0; i < 20; i++)              {                 
string  name =  string .Format( "btnThreadCore_Click{0}" , i);                  Action< object > act = t =>                  {                      try                      {                          Thread.Sleep(2000);                          if  (t.ToString().Equals( "btnThreadCore_Click11" ))                          {                              throw  new  Exception( string .Format( "{0} 執行失敗" , t));                          }                          if  (t.ToString().Equals( "btnThreadCore_Click12" ))                          {                              throw  new  Exception( string .Format( "{0} 執行失敗" , t));                          }                          Console.WriteLine( "{0} 執行成功" , t);                      }                      catch  (Exception ex)                      {                          Console.WriteLine(ex.Message);                      }                  };                 taskList.Add(taskFactory.StartNew(act, name));              }             Task.WaitAll(taskList.ToArray());              #endregion

  

2、執行緒取消。

Task不能主動取消,就好比向CPU發起了一個請求,但是你中途想中斷這個請求,在正常情況下是做不到的,

同樣,執行緒也做不到這一點,只有通過檢測訊號量的方式,來檢測,使其執行緒本身來做。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 CancellationTokenSource cts =  new  CancellationTokenSource();                  for  ( int  i = 0; i < 40; i++)                {                    string  name =  string .Format( "btnThreadCore_Click{0}" , i);                    Action< object > act = t =>                    {                        try                        {                                                       Thread.Sleep(2000);                            if  (t.ToString().Equals( "btnThreadCore_Click11" ))                            {                                throw  new  Exception( string .Format( "{0} 執行失敗" , t));                            }                            if  (t.ToString().Equals( "btnThreadCore_Click12" ))                            {                                throw  new  Exception( string .Format( "{0} 執行失敗" , t));                            }                            if  (cts.IsCancellationRequested)                            {                                Console.WriteLine( "{0} 放棄執行" , t);                            }                            else                            {                                Console.WriteLine( "{0} 執行成功" , t);                            }                        }                        catch  (Exception ex)                        {                            cts.Cancel();                            Console.WriteLine(ex.Message);                        }                    };                    taskList.Add(taskFactory.StartNew(act, name); //沒有啟動的任務  在Cancel後放棄啟動                }                Task.WaitAll(taskList.ToArray());

  

通過程式碼執行可以看到會出現三種結果,那麼這三種結果是什麼情況下出現的呢,

執行成功和執行失敗這兩種情況應該好理解,,放棄執行是在執行失敗出現時,捕獲住了異常資訊,然後通過cts.Cancel();使訊號量改變,

然後通過cts.IsCancellationRequested判斷,這就出現了只要是出現了執行失敗,後面都是放棄執行的情況。

3、多執行緒臨時變數

1 2 3 4 5 6 7 8 9 10 for  ( int  i = 0; i < 5; i++)       {                   new  Action(() =>           {               //Thread.Sleep(100);               Console.WriteLine(i);                       }).BeginInvoke( null null );       }

執行這麼一段關鍵程式碼,會出現什麼樣的結果呢。

出現5個5,這是為什麼呢,怎麼和我們想的不一樣,按理說不應該是出現0、1、2、3、4這樣的數?

這裡因為for迴圈是一定會比執行緒呼叫快,每一遍迴圈完,只是提交了執行緒,還沒有呼叫,當呼叫時,迴圈已經結束,額呼叫時只會取最後i的值。

這就會出現5個5的情況,那麼如何才能出現我們想要的結果呢。

1 2 3 4 5 6 7 8 9 10 for  ( int  i = 0; i < 5; i++) {      int  k = i;      new  Action(() =>      {          Thread.Sleep(100);         // Console.WriteLine(i);          Console.WriteLine(k);      }).BeginInvoke( null null ); }

只需要在迴圈體中加一個變數儲存i的值,就可以了。 

4、執行緒安全 lock

關於執行緒安全,有的人太過於重視,而也有的人一點也不關心。那麼我們應該怎麼做執行緒安全呢。

1 2 3 4 5 6 7 8 9 10 11 private  int  TotalCount = 0;             <br>                for  ( int  i = 0; i < 10000; i++) {          taskList.Add(taskFactory.StartNew(() =>          {                                          this .TotalCount += 1;                              })); } Task.WaitAll(taskList.ToArray());   Console.WriteLine( this .TotalCount);

先來看看這段程式碼,可能大多數人會認為結果會是10000這樣的結果,但是事實呢

你會發現,第一次是9997,第二次9998,第三次是9996,沒有一次出現我們想要的結果。這又是為什麼,

因為我們宣告的private int TotalCount = 0,是共有變數,所有的執行緒都是呼叫同一個,這就出現了執行緒安全的問題。

那麼我們應該如何解決這種情況呢,這就要加一把鎖。

                           private static object btnThreadCore_Click_Lock = new object();

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <em id= "__mceDel" >                for  ( int  i = 0; i < 10000; i++)                  {                                      taskList.Add(taskFactory.StartNew(() =>                          {                                                           lock  (btnThreadCore_Click_Lock)                              {                                  this .TotalCount += 1;                                                              }                          }));                  }                  Task.WaitAll(taskList.ToArray());                    Console.WriteLine( this .TotalCount);                </em> 

這樣在執行相加的時候只會允許一個執行緒進行相加。

 

講完這四點,再來說說Await/Async,這兩個一般都是同時出現

 1、只出現Async,會出現一個警告,合普通執行緒沒什麼區別。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 private  static  async  void  NoReturnNoAwait()        {            //主執行緒執行            Console.WriteLine( "NoReturnNoAwait Sleep before Task,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);            Task task = Task.Run(() => //啟動新執行緒完成任務            {                Console.WriteLine( "NoReturnNoAwait Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);                Thread.Sleep(3000);                Console.WriteLine( "NoReturnNoAwait Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);            });              //主執行緒執行            Console.WriteLine( "NoReturnNoAwait Sleep after Task,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);        }

  

同時出現

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private  static  async  void  NoReturn() {      //主執行緒執行      Console.WriteLine( "NoReturn Sleep before await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);      TaskFactory taskFactory =  new  TaskFactory();      Task task = taskFactory.StartNew(() =>      {          Console.WriteLine( "NoReturn Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);          Thread.Sleep(3000);          Console.WriteLine( "NoReturn Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);      });      await task;      //子執行緒執行   其實是封裝成委託,在task之後成為回撥(編譯器功能  狀態機實現)      Console.WriteLine( "NoReturn Sleep after await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId); }

  

帶有await時,後面執行時,會發現也是子執行緒在執行。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private  static  async Task NoReturnTask()       {           //這裡還是主執行緒的id           Console.WriteLine( "NoReturnTask Sleep before await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);             Task task = Task.Run(() =>           {               Console.WriteLine( "NoReturnTask Sleep before,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);               Thread.Sleep(3000);               Console.WriteLine( "NoReturnTask Sleep after,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);           });           await task;           Console.WriteLine( "NoReturnTask Sleep after await,ThreadId={0}" , Thread.CurrentThread.ManagedThreadId);