C# 多線程詳解 Part.01(UI 線程、子線程)

基本概念

什麼是進程?
       當一個程序開始運行時,它就是一個進程,進程包括運行中的程序和程序所使用到的內存和系統資源。一個進程至少有一個主線程。算法


什麼是線程?
       線程是程序中的一個執行流,每一個線程都有本身的專有寄存器(棧指針、程序計數器等),但代碼區是共享的,即不一樣的線程能夠執行一樣的函數多線程


什麼是多線程?
       多線程是指程序中包含多個執行流,即在一個程序中能夠同時運行多個不一樣的線程來執行不一樣的任務,也就是說容許單個程序建立多個並行執行的線程來完成各自的任務。app


多線程的好處?
       能夠提升 CPU 的利用率。在多線程程序中,一個線程必須等待的時候,CPU 能夠運行其它的線程而不是等待,這樣就大大提升了程序的效率。函數


多線程的不利方面?
       線程也是程序,因此線程須要佔用內存,線程越多佔用內存也越多。
       多線程須要協調和管理,因此 CPU 須要花時間來跟蹤線程。
       線程之間對共享資源的訪問會相互影響,必須解決競用共享資源的問題。
       線程太多會致使控制太複雜,最終可能形成不少 Bug。spa

static void Main(string[] args)
{
    Thread.CurrentThread.Name = "It's Main Thread";
    Console.WriteLine(Thread.CurrentThread.Name + " [Status:" + Thread.CurrentThread.ThreadState + "]");
}

image

       經過 Thread 類的靜態屬性 CurrentThread 能夠獲取當前正在執行的線程。無論建立了多少個這個類的實例,可是類的靜態屬性在內存中只有一個。很容易理解 CurrentThread 爲何是靜態的--雖然有多個線程同時存在,可是在某一個時刻,CPU 只能執行其中一個!
       在.net framework class library 中,全部與多線程機制應用相關的類都是放在 System.Threading 命名空間中。.net

Thread 類有幾個相當重要的方法:pwa

Start() 啓動線程
Sleep(int) 靜態方法,暫停當前線程指定的毫秒數
Abort() 一般使用該方法來終止一個線程
Suspend() 該方法並不終止未完成的線程,它僅僅掛起線程,之後還可恢復
Resume() 恢復被 Suspend()方法掛起的線程的執行

 

操縱一個線程

static void Main(string[] args)
{
    Console.WriteLine("Thread Start/Stop/Join Sample:");
    
    // 建立一個線程,使之執行 Beta 方法 
    Thread oThread = new Thread(Beta);
 
    // 實際上,Start 方法只是通知 CPU 此線程能夠被執行,但具體執行時機則由 CPU 自行決定。
    oThread.Start();
    while (!oThread.IsAlive)
    {
        Thread.Sleep(1);
    }
 
    oThread.Abort();
    oThread.Join();
    Console.WriteLine();
    Console.WriteLine("Beta has finished");
    try
    {
        Console.WriteLine("Try to restart the Alpha.Beta thread");
        oThread.Start();
    }
    catch (ThreadStateException)
    {
        Console.WriteLine("ThreadStateException trying to restart Alpha.Beta. ");
        Console.WriteLine("Expected since aborted threads cannot be restarted.");
        Console.ReadLine();
    }
}
 
public static void Beta()
{
    while (true)
    {
        Console.WriteLine("Beta is running in its own thread.");
    }
}

image

       試圖用 Thread.Start() 方法從新啓動線程 oThread,但顯然 Abort() 方法帶來的後果是不可恢復的終止線程,因此最後程序會拋出 ThreadStateException 異常。線程

 

       線程的優先級

       當線程之間爭奪 CPU 時,CPU 按照線程的優先級給予服務。在 C# 應用程序中,用戶能夠設定 5 個不一樣的優先級,由高到低分別是 HighestAboveNormalNormalBelowNormalLowest在建立線程時若是不指定優先級,那麼系統默認爲 ThreadPriority.Normal。
       經過設定線程的優先級,咱們能夠安排一些相對重要的線程優先執行,例如對用戶的響應等等。3d

static void Main(string[] args)
{
    Thread t1 = new Thread(() =>
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        for (int i = 0; i < 20000; i++)
        {
            // 模擬耗時工做
            var obj = new { name = "XXX", age = 37 };
            GC.Collect();
        }
        watch.Stop();
        Console.WriteLine("t1 finished[ {0} ]", watch.ElapsedMilliseconds);
    });
 
    Thread t2 = new Thread(() =>
    {
        System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
        watch.Start();
        for (int i = 0; i < 20000; i++)
        {
            var obj = new { name = "XXX", age = 37 };
            GC.Collect();
        }
        watch.Stop();
        Console.WriteLine("t2 finished[ {0} ]", watch.ElapsedMilliseconds);
    });
    t1.Priority = ThreadPriority.AboveNormal;
    t2.Priority = ThreadPriority.BelowNormal;
    t1.Start();
    t2.Start();
}

image

       線程的調度算法很是複雜。記住,優先級高的線程並不必定先執行,但 CPU 會將更多的時間片分給優先級高的線程,所以,在相同任務量的前提下,高優先級線程將會較快的完成任務。指針

 

 

Winform 中多線程的應用

       在 Winform 程序總,通常稱繪製窗體和響應用戶的線程爲主線程,或 UI 線程。單線程最顯著的缺點是,當一個事件發生,程序進行一個耗時的運算動做時,UI 線程會出現假死現象,此時會無視對用戶的響應。

       下面的代碼會模擬一些不一樣的狀況:

void DoSomething()
{
    for (int i = 0; i < 900000000; i++)
    {
 
    }
    MessageBox.Show("It's finished.");
}
 
void ShowStr(object obj)
{
    var list = obj as List<string>;
    if (list != null)
    {
        foreach (var item in list)
        {
            MessageBox.Show(item.ToString());
        }                
    }
    else
        MessageBox.Show("null");
}
 
// UI 單線程,運行時窗體會卡死一段時間
private void btnUI_Click(object sender, EventArgs e)
{
    DoSomething();
}
 
// 調用無參函數,此時窗體能響應用戶
private void btnThreadA_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(DoSomething);
    thread.Start();
}
 
// 當全部前臺線程都關閉時,後臺線程將當即結束運行,無條件的關閉
// 而前臺線程運行時,即便關閉 Form 主程序,該線程仍將繼續運行,直到計算完畢
private void btnThreadB_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(DoSomething);
    thread.IsBackground = true;
    thread.Start();
}
 
// 調用有參函數
private void btnThreadC_Click(object sender, EventArgs e)
{
    Thread thread = new Thread(ShowStr);
    thread.Start(new List<string> { "Jacky", "Skysoot", "Sam" });
}

image

       要注意的是,線程在調用有參函數時,經過 Start() 方法傳遞了參數給指定委託,該委託又將參數傳遞給了該線程欲運行的函數。看微軟 Thread 類定義的元數據:

image 

       Thread 類的 4 個構造函數基本分爲 2 類,有參和無參。而 ParameterizedThreadStart 委託定義的方法原型的參數爲 Object 類型,這提升了傳參最大的靈活性。固然,在被調用的函數內部,須要依據必定的約定將 Object 對象進行轉型處理。

 

 

繞圈子封裝一個線程類進行函數和參數的傳遞

// 可定義各種型委託 示例暫定一個
public delegate void Do(object obj);
 
public class Worker
{
    Do method;
    object obj;
 
    private void Work()
    {
        method(obj);
    }
 
    // 建立工人線程時 new 出工人實例 並在線程上指定 Work()
    public static Thread CreateWorkerThread(Do method, object obj)
    {
        Worker worker = new Worker();
        worker.method = method;
        worker.obj = obj;
        Thread t = new Thread(worker.Work);
        return t;
    }
}
 
// 任務類
public class Quest
{
    public static void Quest1(object obj)
    {
        Console.WriteLine("工人開始:" + obj.ToString() + "\r\n");
    }
 
    public static void Quest2(object obj)
    {
        string[] list = obj as string[];
        if (obj != null)
        {
            foreach (var item in list)
            {
                Console.WriteLine("工人開始:" + item);
            }
        }
    }
}
 
public class Test
{
    public static void Main(string[] args)
    {
        Thread t1 = Worker.CreateWorkerThread(Quest.Quest1, "搬磚");
        t1.Start();
        Thread t2 = Worker.CreateWorkerThread(Quest.Quest2, new string[] {"唱歌", "跳舞", "打檯球" });
        t2.Start();
    }
}

       這種封裝只是一種啓發的方式而已,並不是模式。但封裝委託後的好處在於,調用方能夠靈活指定 Worker 類執行什麼類型的任務,加工什麼參數,而無需再去考慮其他事情。

相關文章
相關標籤/搜索