【C#多執行緒】1.Thread類的使用及注意要點

  • 2019 年 11 月 5 日
  • 筆記

Thread隨便講講

  因為在C#中,Thread類在我們的新業務上並不常用了(因為創建一個新執行緒要比直接從執行緒池拿執行緒更加耗費資源),並且在.NET4.0後新增了Task類即Async與await關鍵字,使得我們基本不再用Thread了,不過在學習多執行緒前,有必要先了解下Thread類,這裡就先隨便講講Thread。

1.使用多執行緒的幾種方式

  多執行緒Thread類只支援運行兩種方法,一種是無參數並且無返回值的方法,第二種是有一個Object類型參數(有且只能有一個參數,並且必須是Object類型)且無返回值的方法。如果想讓多執行緒方法攜帶多個參數,可以將多個參數放入一個集合或數組中傳入方法。

  下面例子使用了控制台來演示多執行緒的簡單使用:

using System;  using System.Threading;    namespace ConsoleApplication1  {      class Program      {          //無參數無返回值方法          public static void DoSomething()          {              for (int i = 0; i < 100; i++)              {                  Thread.Sleep(500);              }          }          //有參數無返回值方法          public static void DoSomethingWithParameter(object obj)          {              for (int i = 0; i < (int)obj; i++)              {                  Console.WriteLine(Thread.CurrentThread.ManagedThreadId);                  Thread.Sleep(500);              }          }              static void Main(string[] args)          {              //獲取主執行緒ID              int currentThreadId = Thread.CurrentThread.ManagedThreadId;              Console.WriteLine($"---------主執行緒<{currentThreadId}>開始運行---------");              //多執行緒運行無參數方法方式1              ThreadStart ts = DoSomething;//ThreadStart是一個無參數,無返回值的委託              Thread thread1 = new Thread(ts);              thread1.Start();                //多執行緒運行無參數方法方式2              Thread thread2 = new Thread(DoSomething);//可省略ThreadStart              thread2.Start();                //多執行緒運行有參數方法方式1              //ParameterizedThreadStart是一個有一個Object類型參數,但是無返回值的委託。              ParameterizedThreadStart pts = DoSomethingWithParameter;              Thread thread3 = new Thread(pts);              thread3.Start(100);                //多執行緒運行有參數方法方式2              //可以省略ParameterizedThreadStart              Thread thread4 = new Thread(DoSomethingWithParameter);              thread4.Start(100);                //還可以使用lamda表達式簡化多執行緒寫法              new Thread(() =>              {                  for (int i = 0; i < 100; i++)                  {                      Console.WriteLine(Thread.CurrentThread.ManagedThreadId);                      Thread.Sleep(500);                  }              }).Start();              Console.WriteLine($"---------主執行緒<{currentThreadId}>運行結束---------");          }      }  }

  運行結果如下:

  

 

 

 

 2.前台執行緒與後台執行緒

  • 前台執行緒

  如主執行緒(或稱為UI執行緒)就是前台執行緒,默認Thread的實例均為前台執行緒,前台執行緒的特點是,如果當前應用的前台執行緒沒有全部運行完畢,那麼當前應用就無法退出。舉個例子,我們知道正常情況下,控制台應用在Main方法結束後會自動結束當前進程,如果我們在Main方法中創建了一個新Thread執行緒,並使其保持運行,那麼即使Main方法執行完畢,控制台進程也無法自動關閉(除非手動右上角點×)。就如下圖情況,畫紅圈的地方表示Main方法執行完畢,可是程式依舊在運行,所以我們一般在用Thread的時候會將Thread設置為後台執行緒。

 

 

 

  • 後台執行緒

  後台執行緒與前台執行緒的唯一區別是,它不會去影響程式的生老病死,當程式的前台執行緒全部關閉(即程式退出),那麼即使程式的後台執行緒依舊在執行任務,那麼也會強制關閉。

  設置Thread為後台執行緒的方式:

        Thread tt = new Thread(DoSomething);          tt.IsBackground = true;//設置tt為後台執行緒          tt.Start();

  前台執行緒與後台執行緒對程式的影響效果看似好像不算大,但是如果我們在做Winform或者WPF項目時,若在某窗體內執行一個新執行緒任務(這個新執行緒是前台執行緒),如果在任務執行期間關閉程式,此時會發現,雖然介面都被關閉,但是電腦任務管理器中此程式依舊還在運行(並且如果在新執行緒中執行的任務異常導致執行緒無法關閉,那麼這個程式就會一直在後台跑下去),再次開啟程式可能會導致打不開等後果,這種行為是非常不好的。所以我們一般使用多執行緒Thread類時,最好順手將它設置為後台執行緒。我們可以舉個例子。

        static void Main(string[] args)          {              //獲取主執行緒ID              int currentThreadId = Thread.CurrentThread.ManagedThreadId;              Console.WriteLine($"---------主執行緒<{currentThreadId}>開始運行---------");                //執行一個大概可以運行50秒的新執行緒              Thread t = new Thread(() =>              {                  for (int i = 0; i < 100; i++)                  {                      Console.WriteLine(Thread.CurrentThread.ManagedThreadId);                      Thread.Sleep(500);                  }              });              t.IsBackground = true;//設置t為後台執行緒              t.Start();                Console.WriteLine($"---------主執行緒<{currentThreadId}>運行結束---------");          }

  這個例子的運行結果就不截圖了,因為控制台會一閃而過(立即執行完Main方法便關閉),即使後台執行緒t還在執行任務,但是也會強制關閉。

 

3.讓主執行緒等待新執行緒執行完成後再繼續執行(使用Thread的Join方法)

  直接上程式碼:

        static void Main(string[] args)          {              //獲取主執行緒ID              int currentThreadId = Thread.CurrentThread.ManagedThreadId;              Console.WriteLine($"---------主執行緒<{currentThreadId}>開始運行---------");                //執行一個大概可以運行50秒的新執行緒              Thread t = new Thread(() =>              {                  for (int i = 0; i < 20; i++)                  {                      Console.WriteLine(Thread.CurrentThread.ManagedThreadId);                      Thread.Sleep(500);                  }              });              t.IsBackground = true;//設置t為後台執行緒              t.Start();                t.Join();//在t執行緒執行期間,如果主執行緒調用t執行緒的Join方法,主執行緒會卡在這個地方直到t執行緒執行完畢                Console.WriteLine($"---------主執行緒<{currentThreadId}>運行結束---------");          }

 

4.Thread實例的其他常用方法

  直接看程式碼注釋吧:

        static void Main(string[] args)          {              //執行一個大概可以運行50秒的新執行緒              Thread t = new Thread(DoSth);              t.IsBackground = true;//設置t為後台執行緒              t.Start();                t.Join();//在t執行緒執行期間,如果主執行緒調用t執行緒的Join方法,主執行緒會卡在這個地方知道t執行緒執行完畢              t.Priority = ThreadPriority.Normal;//設置執行緒調度的優先順序              ThreadState rhreadState = t.ThreadState;//獲取執行緒運行狀態。              bool b = t.IsAlive;//獲取執行緒當前是否存活              t.Interrupt();//中斷當前執行緒              t.Abort();//終止執行緒                    }

 

5.Thread類的常用方法

  直接看程式碼注釋吧:

        static void Main(string[] args)          {              //使得當前執行緒暫停1秒再繼續執行,此處會暫停主執行緒1秒鐘              //如果寫在其他執行緒執行的方法中,會讓執行那個方法的執行緒暫停1秒再繼續執行)              Thread.Sleep(1000);                //獲取當前執行執行緒的執行緒實例              Thread t = Thread.CurrentThread;          }

  

  

  下節我們會簡單講講執行緒池+同步回調+非同步回調+跨執行緒訪問UI。