在上篇文章中我們已經知道了多線程是什麼了,那麼它到底可以幹嘛呢?這裡特別聲明一個前面的委托沒看的同學可以到上上上篇博文查看,因為多線程要經常使用到委托。源碼 一、非同步、同步 1.同步(在計算的理解總是要你措不及防,同步當線程做完一件事情之後,才會執行後續動作),同步方法慢,只有一個線程執行,非同步方法 ...
在上篇文章中我們已經知道了多線程是什麼了,那麼它到底可以幹嘛呢?這裡特別聲明一個前面的委托沒看的同學可以到上上上篇博文查看,因為多線程要經常使用到委托。源碼
一、非同步、同步
1.同步(在計算的理解總是要你措不及防,同步當線程做完一件事情之後,才會執行後續動作),同步方法慢,只有一個線程執行,非同步方法快,因為多個線程一起幹活,但是兩者並不是線性增長,當我們的非同步線程占有的資源越來越多了,會導致資源可能不夠,其次線程過多CPU也是需要管理成本的,所以不是越多越好。
2.非同步(可以同時執行多個任務,在同樣的時間,執行不同的任務),同步方法卡界面(UI),因為我們的主線程(UI)忙於計算造成了堵塞了。非同步方法不卡界面,計算任務交給了子線程完成。winform中體現的玲玲精緻。(你品,你細品),web 可以非同步的處理一起其他的任務,比如給用戶發郵箱(我們的BS結構的,每次訪問都是一個子線程,當我們的代碼寫的比較糟糕,是不是載入比較慢呢哈哈)。非同步多線程無序,執行的先後無序,執行的時間不確定,結束也不確定,所以我們很難通過執行時間和先後順序控制,非同步的執行順序。
二、初識Thread
屬性名稱 | 說明 |
---|---|
CurrentContext | 獲取線程正在其中執行的當前上下文。 |
CurrentThread | 獲取當前正在運行的線程。 |
ExecutionContext | 獲取一個 ExecutionContext 對象,該對象包含有關當前線程的各種上下文的信息。 |
IsAlive | 獲取一個值,該值指示當前線程的執行狀態。 |
IsBackground | 獲取或設置一個值,該值指示某個線程是否為後臺線程。 |
IsThreadPoolThread | 獲取一個值,該值指示線程是否屬於托管線程池。 |
ManagedThreadId | 獲取當前托管線程的唯一標識符。 |
Name | 獲取或設置線程的名稱。 |
Priority | 獲取或設置一個值,該值指示線程的調度優先順序。 |
ThreadState | 獲取一個值,該值包含當前線程的狀態。 |
Thread 中包括了多個方法來控制線程的創建、掛起、停止、銷毀,後面的例子中會經常使用。
方法名稱 | 說明 |
---|---|
Abort() | 終止本線程。 |
GetDomain() | 返回當前線程正在其中運行的當前域。 |
GetDomainId() | 返回當前線程正在其中運行的當前域Id。 |
Interrupt() | 中斷處於 WaitSleepJoin 線程狀態的線程。 |
Join() | 已重載。 阻塞調用線程,直到某個線程終止時為止。 |
Resume() | 繼續運行已掛起的線程。 |
Start() | 執行本線程。 |
Suspend() | 掛起當前線程,如果當前線程已屬於掛起狀態則此不起作用 |
Sleep() | 把正在運行的線程掛起一段時間。 |
1.Thread是我們.NET 1.0 給我們提供的多線程類,可以創建,和控制多線程,Thread類構造函數為接受ThreadStart和ParameterizedThreadStart類型的委托參數,下麵有請代碼神君。
/// <summary> /// 使用Thread 創建多線程 /// </summary> public static void Show() { //實例化創建線程 無參無返回值 Thread thread = new Thread(() => { Console.WriteLine("我是多線程"); }); thread.Start(); //創建5個線程1 for (int i = 0; i < 5; i++) { //這個之所以創建一個k,後麵線程不安全會說到 var k = i; //這是一個有參數無返回值多線程 new Thread(x => Running(Convert.ToInt32(x))).Start(k); } Console.Read(); } /// <summary> /// 一個執行需要長時間的任務 /// </summary> static void Running(int s) { Console.WriteLine("**********************************"); Console.WriteLine("執行開始啦" + s); Console.WriteLine("獲取當前執行的線程ID:" + Thread.CurrentThread.ManagedThreadId.ToString()); var j = 0; for (int i = 0; i < 1000000000; i++) { j++; } Console.WriteLine("執行結束啦" + s); }View Code
二、漸入佳境
1.運行上面的代碼,可以看到線程的無序性,雖然我們的0最先開始執行的,但是不是第一個結束的,這個是因為我們每個線程執行的時間的不確定性。這裡也要特別說明為什麼Thread構造函數傳遞的是ThreadStart和ParameterizedThreadStart類型的委托參數,為什麼不是Action ,Func,答案就是.NET 1.0的時候還沒有Action 、Func。ThreadStart委托是一個無參無返回值上代碼中我們創建了,ParameterizedThreadStart委托是一個有參數無返回值,但是我們可以看到我們的參數是一個object類型,是一個不安全的參數(當時泛型也沒有出來)當然為了防止這問題,我們也是想到了方法,那就是我們可以通過一個泛型類,幫我們限制參數類型。
/// <summary> /// 防止參數不安全 /// </summary> public static void Show5() { //我們創建一個泛型類,限制我們的類型 MyThread<string> mythread = new MyThread<string>("Thread_child"); //將我們的方法傳遞,進去 Thread th3 = new Thread(mythread.ThreadChild); //啟動線程 th3.Start(); } /// <summary> /// 創建一個泛型類 /// </summary> /// <typeparam name="T"></typeparam> class MyThread<T> { private T data; public MyThread(T data) { this.data = data; } public void ThreadChild() { Console.WriteLine("Child Thread Start! Result:{0}", data); } }View Code
2.我們在上面還提供了其他的方法,但是這些方法已經不建議使用了,現在已經棄用了,因為我們無法精確地控制線程的開啟與暫停,當我們將線程掛起的時候,同時也會掛起線程使用的資源,會導致死鎖,不建議使用。將線程銷毀也不建議 不一定及時/有些動作發出收不回來。(這裡我使用的是.net Core 3.1 執行直接報錯了哈哈)
/// <summary> /// 使用Thread 線程掛起、喚醒線程、銷毀,方式是拋異常、取消Abort異常 /// </summary> public static void Show1() { //創建一個Thread 線程 Thread thread = new Thread(() => { Running(); }); //開啟線程 thread.Start(); //這個是線程掛起 //thread.Suspend(); //喚醒線程 //thread.Resume(); //上面的兩個方法,現在已經棄用了,因為我們無法精確地控制線程的開啟與暫停 //當我們將線程掛起的時候,同時也會掛起線程使用的資源,會導致死鎖,不建議使用 try { //將線程銷毀 //也不建議 不一定及時/有些動作發出收不回來 thread.Abort(); } catch (Exception) { //靜態方法將線程異常取消繼續工作 Thread.ResetAbort(); } Console.Read(); }View Code
3.線程優先順序,當然我們的線程是一個無序的,也有控制線程執行的權重,但是這個優先順序不是絕對的,因為線程的執行順序還是看我們的CPU爸爸的,但是我們可以利用Priority屬性做線程的權重執行,使用也很簡單
/// <summary> /// 使用Thread 線程的優先順序(但是執行還是看CPU,可以做優先順序,但是不是絕對優先) /// </summary> public static void Show3() { //創建一個Thread 線程 Thread thread = new Thread(() => { Running(); }); thread.Start(); //thread.Priority屬性可以設置線程的優先順序關係 thread.Priority = ThreadPriority.Highest; Console.WriteLine("執行完啦啦啦啦啦啦啦啦啦啦啦拉拉"); Console.Read(); }View Code
4.前臺線程、後臺線程(這個字面意思,還是和我們的理解是不一樣的)我們設置IsBackground控制線程是否(前/後)台線程。預設是前臺線程,啟動之後一定要完成任務的,阻止進程退出。指定後臺線程:隨著進程退出。
三、多線程起飛
1、非同步回調
1.我們的Thread沒有給我提供非同步回調的功能,沒辦法需要自己造輪子了,我們可以先想一下回調的需求是什麼,需求分析:當我們的線程任務執行完之後需要之後某些方法。我們細品一下,我們要執行完之後,在執行一個人任務,那就是同步執行非同步方法了吧。我們在子線程中怎麼同步執行呢?下麵的代碼就實現了回調功能不管我們執行多少次回調總會在任務後面執行。
/// <summary> /// 非同步回調執行 /// </summary> public static void Show6() { //創建一個任務委托 ThreadStart threadStart = () => { Console.WriteLine("我是任務"); }; //創建一個回調執行的委托 Action action = () => { Console.WriteLine("哈哈,我就是你們的回調方法哈,記得雙擊麽麽噠"); Console.WriteLine("*********************************************"); }; ThreadWithCallback(threadStart, action); Console.ReadLine(); } /// <summary> /// 回調封裝 無返回值 /// </summary> /// <param name="start"></param> /// <param name="callback">回調</param> private static void ThreadWithCallback(ThreadStart start, Action callback) { Thread thread = new Thread(() => { start.Invoke(); callback.Invoke(); }); thread.Start(); }View Code
2、返回參數
1.當然我們使用線程需要返回參數,但是我們的Thread沒有給我們提供返回值的委托和方法,這個要莫子搞羅?當然我們先分析需求,我們要獲取返回值是不是要等線程執行之後呢?好的線程執行我們可以使用Join堵塞線程等它執行完畢,但是我們要怎麼獲取返回值呢?對了我們可以創建一個變數,我們的線程給變數賦值嗎?
/// <summary> /// 非同步返回值 /// </summary> public static void Show7() { //創建一個委托 Func<string> func = () => { return "我是返回值"; }; //獲取執行結果 Console.WriteLine(ThreadWithReturn(func).Invoke()); Console.ReadLine(); } /// <summary> /// 有返回值封裝(請根據本案例自行封裝回調) /// </summary> /// <typeparam name="T">返回值類型</typeparam> /// <param name="func">需要子線程執行的方法</param> /// <returns></returns> private static Func<T> ThreadWithReturn<T>(Func<T> func) { //初始化一個泛型,限制我們的類型 T t = default(T); ThreadStart newStart = () => { //線程給變數賦值 t = func.Invoke(); }; //創建線程 Thread thread = new Thread(newStart); //執行線程 thread.Start(); //創建一個委托 無參有返回值,執行委托會發生執行線程等待堵塞 //當線程執行完之後,也就是說線程已經給變數t賦值了,我們就返回t return new Func<T>(() => { thread.Join(); return t; }); }View Code
四、Thread總結
1.大家是不是覺得多線程很酷呢?哈哈我剛剛學的時候也是激動的心顫抖的手。當然文章中我們介紹了很多API的使用,大家可以動手試試,API的使用是小事,最重要的是我們的思路,到我們看到回調封裝和返回值封裝,我們都是利用了多線程的一些特性,來完成的這些功能拓展的。我們巨集觀的看多線程感覺很恐怖,但是在我們做回調函數的時候是不是感覺有一種微觀看法,線程執行的內部也是同步的執行哪些方法的。好了今天就寫到這裡昨天晚上9點多就睡了,早起擼個文章美滋滋。當然多線程還有講完的,才說道了.NET 1.0哈哈,後續的文章也會寫出來。