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