C# 多執行緒技術
這節講一下多執行緒(Thread)技術。
在講執行緒之前,先區分一下程式,程序,執行緒三者的區別,大體上說,一個程式可以分為多個程序,一個程序至少由一個執行緒去執行,它們是層層包含的關係。我們寫的程式,就是一個程序,多個程序,以完成一個使用者服務,或者完成一個大的介面展現,就組成一個程式,但在CPU層面,只有執行緒的概念,執行緒是最小的執行單位,Windows中採用CPU輪換制度,CPU給每個要執行的執行緒分配操作時間,輪流執行,但因為CPU的主頻實在是太高,我們感受不到每個程式輪空期卡頓。
一個程序,開了一條執行緒去執行,那麼這個執行緒就是主執行緒,一般在UI程式中,如果主執行緒執行CPU密集型的耗時工作(如IO操作),那麼就會導致介面處於”假死“狀態,直到主執行緒完成這個耗時的任務,所以,我們需要解決這種假死的問題,以帶給使用者更好的互動體驗,那麼就要用到多執行緒技術,將耗時的工作,交給後臺執行緒執行。
建立執行緒
使用Thread類建立執行緒,該類位於System.Thread類之下,必須在建立之時給填入執行方法,或者填入lambda表示式。
Thread thread=new Thread(() => { for (int i = 0; i <= 100; i++) { Console.WriteLine($"{Thread.CurrentThread.Name}=========>{i}"); } });
以上是建立了一個執行緒,並填入一個lambda表示式,輸出當前執行緒的名稱和0-100,注意,建立並不是啟動,啟動需要呼叫Start()方法。
下面看一段完整的程式碼:
Thread thread=new Thread(() => { Console.WriteLine(Thread.CurrentThread.IsAlive); for (int i = 0; i <= 100; i++) { Console.WriteLine($"{Thread.CurrentThread.Name}=========>{i}"); } }); thread.Name = "BackgroundThread"; thread.IsBackground= true; thread.Start(); for (int i = 0; i <= 100; i++) { Console.WriteLine($"main=========>{i}"); } Console.WriteLine(thread.ManagedThreadId); Thread.Sleep(200); Console.WriteLine(thread.IsAlive);
先介紹一下程式碼中出現的幾個屬性和方法:
Name屬性,故名思意,這是為執行緒起一個名字,
IsBackground屬性,設定執行緒是否是後臺執行緒,如果前臺執行緒也就是主執行緒結束執行,它所有的後臺執行緒也會立即終止。
IsAlive只讀bool屬性,標識當前執行緒是否執行完畢,這裡要說一下執行緒的生命週期,它的宣告週期是從開始執行到結束,執行完交給執行緒的程式碼執行緒立即dead掉,也可以強行掛起執行緒,但這個方法已被捨棄,因為強行掛起執行緒有弊端,就像在高速上跑的小汽車,不能隨便就直接攔截。
ManagedThreadId屬性,獲取當前執行緒ID
Thread.CurrentThread屬性,獲取當前的執行緒。
Thread.Sleep(毫秒值)方法,執行到此方法,執行緒會睡眠,哪個執行緒執行,哪個執行緒睡,此處讓其睡200毫秒,為了展示IsAlive屬性。
Start()方法,是讓這個執行緒啟動。
接下來看一下執行結果(為了方便檢視結果,我把迴圈都調成了6次):
可以看到,執行中的IsAlive屬性是true,睡了200秒,執行緒執行完畢,IsAlive屬性變為flase。
Join方法
執行緒呼叫join()方法,是指示CPU該執行緒交出自己的執行權(也就是該執行緒處於阻塞狀態),直到其它執行緒執行執行完畢,Join()方法有個毫秒值過載,用於設定交出執行權多少時間。將上方程式碼更改後:
Thread thread=new Thread(() => { for (int i = 0; i <= 20; i++) { Console.WriteLine($"{Thread.CurrentThread.Name}=========>{i}"); if (i == 10) { Thread.CurrentThread.Join(200); } } }); thread.Name = "BackgroundThread"; thread.IsBackground = true; thread.Start(); for (int i = 0; i <= 20; i++) { Console.WriteLine($"main=========>{i}"); } Thread.Sleep(200);
執行結果為:
當在i=10的時候,後臺執行緒交出了執行權200毫秒,這期間只有主執行緒在工作。
執行緒的優先順序
執行緒的優先順序是可以設定的,但是,這僅僅是人為了提高了執行緒的優先順序,至於真正的調配還得看CPU,所以一般多執行緒開發,是很繁瑣的事情,維護起來也困難,所以多執行緒技術需要慎用,不能濫用。
執行緒優先順序有個列舉類,原始碼如下:
public enum ThreadPriority { Lowest,//優先順序最低 BelowNormal,//低於正常 Normal,//正常 AboveNormal,//較高 Highest,//最高 }
當我將其優先順序更改為最高時,也並不能決定它是最快執行的,所以優先順序的設定只是理論上的。
執行緒池
執行緒池是系統事先建立好的一堆後臺執行緒,當一個程式需要一個後臺執行緒執行一個不太重要的執行緒,並且程式碼簡短的話,可以使用執行緒池,不用再自己new一個執行緒,這能略微提高效能。
ThreadPool.QueueUserWorkItem(s => Console.WriteLine(s),"helloworld");
使用ThreadPool.QueueUserWorkItem()建立一個執行緒池執行緒,當執行緒池中有空閒執行緒時會取出一個執行緒來執行。須注意的是,第一個引數是一個帶有一個引數的委託WaitCallback,第二個引數會作為這個委託的引數傳入。
public delegate void WaitCallback(object state);
執行緒安全
不得不提的是,多執行緒存線上程安全問題,所以在開發時要注意。何為執行緒安全呢,舉個例子,火車站售票,1000個人同時搶100張票,多執行緒情況下,有可能兩個甚至多個執行緒同時搶到一個票,最後這幾個執行緒都是一個座號,這顯然是不合情理的。解決執行緒安全問題,就必須保證共享資料的同步性,也就是說同一時間只有一個執行緒訪問共享的資料,關於執行緒安全的例子,我會在下一期進行講解。
這是我的公眾號二維碼,獲取最新文章,請關注此號