【C#多執行緒】1.Thread類的使用及注意要點
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; }
下節我們會簡單講講執行緒池+同步回撥+非同步回撥+跨執行緒訪問U