1. 程式人生 > 實用技巧 >C# 多執行緒技術

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張票,多執行緒情況下,有可能兩個甚至多個執行緒同時搶到一個票,最後這幾個執行緒都是一個座號,這顯然是不合情理的。解決執行緒安全問題,就必須保證共享資料的同步性,也就是說同一時間只有一個執行緒訪問共享的資料,關於執行緒安全的例子,我會在下一期進行講解。

這是我的公眾號二維碼,獲取最新文章,請關注此號