多線程部分
多線程在4.0中被簡化了很多,僅僅只需要用到System.Threading.Tasks.::.Task類,下面就來詳細介紹下Task類的使用。
一、簡單使用
開啟一個線程,執行循環方法,返回結果。開始線程為Start(),等待線程結束為Wait()。
- ??????? /// <summary>
- ??????? /// Task簡單使用
- ??????? /// </summary>
- ??????? private void Demo1()
- ??????? {
- ??????????? int i = 0;
- ??????????? Random r = new Random ( DateTime .Now.Second);
- ??????????? Task t = new Task (() =>
- ??????????? {
- ??????????????? for ( int v = 0; v < 100; v++)
- ??????????????????? i += r.Next(100);
- ??????????? });
- ??????????? t.Start();
- ??????????? t.Wait();
- ??????????? Console .WriteLine( "這是執行Task1后等待完成:" + i.ToString());
- ??????????? Console .ReadLine();
- ??????? }
比以前使用Thread方便多了吧。
上面的例子是使用外部的變量獲得結果,下面的例子是用Task<T>直接返回結果,當調用Result屬性時,會自動等待線程結束,等同調用了Wait()。代碼如下:
- ??????? /// <summary>
- ??????? /// Task帶返回值
- ??????? /// </summary>
- ??????? private void Demo2()
- ??????? {
- ??????????? Random r = new Random ( DateTime .Now.Second);
- ??????????? Task < int > t = new Task < int >(() =>
- ??????????? {
- ??????????????? int i = 0;
- ??????????????? for ( int v = 0; v < 100; v++)
- ??????????????????? i += r.Next(100);
- ??????????????? return i;
- ??????????? });
- ??????????? t.Start();
- ??????????? Console .WriteLine( "這是執行Task1獲取返回值:" + t.Result.ToString());
- ??????????? Console .ReadLine();
- ??????? }
總結1:Task的使用比Thread簡單很多,減少了同步,等待等等問題,唯一的遺憾是不支持Thread的IsBackground。
結論1:如果不需要使用IsBackground,那么盡情的使用Task吧。
?
?
二、線程執行完畢后調用另一個線程
也就是兩個線程,有序的執行,這里使用ContinueWith(),
t執行完畢后再執行一個task方法,不多說了代碼如下:
- ??????? /// <summary>
- ??????? /// Task 執行完畢后調用另一個Task
- ??????? /// </summary>
- ??????? private void Demo3()
- ??????? {
- ??????????? Random r = new Random ( DateTime .Now.Second);
- ??????????? Task < int > t = new Task < int >(() =>
- ??????????? {
- ??????????????? int i = 0;
- ??????????????? for ( int v = 0; v < 100; v++)
- ??????????????????? i += r.Next(100);
- ??????????????? return i;
- ??????????? });
- ??????????? t.ContinueWith(( Task < int > task) =>
- ??????????? {
- ??????????????? Console .WriteLine( "這是執行完畢Task1后繼續調用Task2:" + task.Result.ToString());
- ??????????? });
- ??????????? t.Start();
- ??????????? Console .ReadLine();
- ??????? }
也可以直接鏈式的寫下去,代碼如下:
- ??????? /// <summary>
- ??????? /// Task 執行完畢后調用另一個Task(鏈式寫法)
- ??????? /// </summary>
- ??????? private void Demo4()
- ??????? {
- ??????????? Random r = new Random ( DateTime .Now.Second);
- ??????????? Task < int > t = new Task < int >(() =>
- ??????????? {
- ??????????????? int i = 0;
- ??????????????? for ( int v = 0; v < 100; v++)
- ??????????????????? i += r.Next(100);
- ??????????????? return i;
- ??????????? });
- ??????????? Task t2 = t.ContinueWith(( Task < int > task) =>
- ??????????? {
- ??????????????? Console .WriteLine(task.Result.ToString());
- ??????????? });
- ??????????? t2.ContinueWith(task =>
- ??????????? {
- ??????????????? Console .WriteLine( "這是執行完畢Task1后繼續調用Task2,Task2后調用Task3。" );
- ??????????? });
- ??????????? t.Start();
- ??????????? Console .ReadLine();
- ??????? }
結論2:Task可以便捷的將幾個方法串行執行。
?
?
三、帶有父子關系的線程/多線程并行開啟
t帶有t1,t2,t3三個子線程,執行t的時候t1,t2,t3可并行處理,t必須等待t1,t2,t3都執行完畢后才能結束。
創建子Task時候必須指定參數為AttachedToParent。
- ??????? /// <summary>
- ??????? /// 帶有父子關系的Task集合,[TaskCreationOptions.AttachedToParent]
- ??????? ///
- ??????? /// 值??????????????? 說明
- ??????? /// None????????????? 默認值,此Task會被排入Local Queue中等待執行,采用LIFO模式。
- ??????? /// AttachedToParent? 建立的Task必須是外圍的Task的子Task,也是放入Local Queue,採LIFO模式。
- ??????? /// LongRunning?????? 建立的Task不受Thread Pool所管理,直接新增一個Thread來執行此Task,無等待、無排程。
- ??????? /// PreferFairness??? 建立的Task直接放入Global Queue中,採FIFO模式。(比上面的優先級低)
- ??????? /// </summary>
- ??????? private void Demo5()
- ??????? {
- ??????????? Task < int > t = new Task < int >(() =>
- ??????????? {
- ??????????????? Task < int > t1 = new Task < int >(() =>
- ??????????????? {
- ??????????????????? int i = 0;
- ??????????????????? Random r = new Random ( DateTime .Now.Second);
- ??????????????????? for ( int v = 0; v < 100; v++)
- ??????????????????????? i += r.Next(100);
- ??????????????????? return i;
- ??????????????? }, TaskCreationOptions .AttachedToParent);
- ??????????????? Task < int > t2 = new Task < int >(() =>
- ??????????????? {
- ??????????????????? int i = 0;
- ??????????????????? Random r = new Random ( DateTime .Now.Second);
- ??????????????????? for ( int v = 0; v < 100; v++)
- ??????????????????????? i += r.Next(100);
- ??????????????????? return i;
- ??????????????? }, TaskCreationOptions .AttachedToParent);
- ??????????????? Task < int > t3 = new Task < int >(() =>
- ??????????????? {
- ??????????????????? int i = 0;
- ??????????????????? Random r = new Random ( DateTime .Now.Second);
- ??????????????????? for ( int v = 0; v < 100; v++)
- ??????????????????????? i += r.Next(100);
- ??????????????????? return i;
- ??????????????? }, TaskCreationOptions .AttachedToParent);
- ??????????????? t1.Start();
- ??????????????? t2.Start();
- ??????????????? t3.Start();
- ??????????????? return t1.Result + t2.Result + t3.Result;
- ??????????? });
- ??????????? t.Start();
- ??????????? t.Wait();
- ??????????? Console .WriteLine( "這是帶有父子關系的Task集合:" + t.Result.ToString());
- ??????????? Console .ReadLine();
- ??????? }
結論3:多個線程的同時開啟在這里也很方便,也不用擔心同步等問題。
?
?
四、Task的中斷
這個很復雜,就不多說了,代碼中有比較詳細的介紹。
- ??????? /// <summary>
- ??????? /// 中途取消Task執行,Token
- ??????? ///
- ??????? /// 一是正常結束、二是產生例外、三是透過Cancel機制,這三種情況都會反映在Task.Status屬性上
- ??????? /// 值????????????????????????????? 說明
- ??????? /// Created???????????????????????? Task已經建立,但未呼叫Start。
- ??????? /// WaitingForActivation??????????? Task已排入排程,但尚未執行(一般我們建立的Task不會有此狀態,只有ContinueWith所產生的Task才會有此狀態)。
- ??????? /// WaitingToRun??????????????????? Task已排入排程,等待執行中。
- ??????? /// Running???????????????????????? Task執行中。
- ??????? /// WaitingForChildrenToComplete??? Task正等待子Task結束。
- ??????? /// RanToCompletion???????????????? Task已經正常執行完畢。
- ??????? /// Canceled??????????????????????? Task已被取消。
- ??????? /// Faulted???????????????????????? Task執行中發生未預期例外。
- ??????? ///
- ??????? /// 除了Status屬性外,Task還提供了另外三個屬性來判定Task狀態。
- ??????? /// 屬性??????????? 說明
- ??????? /// IsCompleted???? Task已經正常執行完畢。
- ??????? /// IsFaulted?????? Task執行中法生未預期例外。
- ??????? /// IsCanceled????? Task已被取消。
- ??????? /// </summary>
- ??????? private void Demo6()
- ??????? {
- ??????????? CancellationTokenSource cts = new CancellationTokenSource ();
- ??????????? var ctoken = cts.Token;
- ??????????? Task t1 = new Task (() =>
- ??????????? {
- ??????????????? for ( int v = 0; v < 10; v++)
- ??????????????? {
- ??????????????????? if (ctoken.IsCancellationRequested)
- ??????????????????? {
- ??????????????????????? //第一種寫法
- ??????????????????????? //這個會拋出異常
- ??????????????????????? ctoken.ThrowIfCancellationRequested();
- ??????????????????????? //另一種寫法
- ??????????????????????? //這個不會返回異常,但是獲取不到是否是中斷還是執行完畢。
- ??????????????????????? //return;
- ??????????????????? }
- ??????????????????? Thread .Sleep(1000);
- ??????????????????? Console .WriteLine(v);
- ??????????????? }
- ??????????? }, ctoken);
- ??????????? t1.Start();
- ??????????? Thread .Sleep(2000);
- ??????????? cts.Cancel();
- ??????????? try
- ??????????? {
- ??????????????? t1.Wait();
- ??????????? }
- ??????????? catch
- ??????????? {
- ??????????????? if (t1.IsCanceled)
- ??????????????????? Console .WriteLine( "cancel" );
- ??????????? }
- ??????????? Console .ReadLine();
- ??????????? cts.Dispose();
- ??????? }
結論4:中斷很復雜,但是對一般邏輯來說,是沒有很大必要的。
?
?
五、其他
這里介紹下另一種寫法Task.Factory,以及ContinueWhenAny和ContinueWhenAll兩個方法。
Task.Factory是靜態工廠類,用于對Task提供一些麻煩的支持,這里主要介紹ContinueWhenAny和ContinueWhenAll。
ContinueWhenAll所指定的函式會在傳入的所有Tasks結束時執行,只會執行一次。
ContinueWhenAny所指定的函式會在傳入的Tasks中有任何一個結束時執行,且與ContinueWhenAll相同,只會執行一次。
好了,還是看代碼:
- ??????? /// <summary>
- ??????? /// Task.Factory
- ??????? /// ContinueWhenAny和ContinueWhenAll
- ??????? /// ContinueWhenAll所指定的函式會在傳入的所有Tasks結束時執行,只會執行一次。
- ??????? /// ContinueWhenAny所指定的函式會在傳入的Tasks中有任何一個結束時執行,且與ContinueWhenAll相同,只會執行一次。
- ??????? /// </summary>
- ??????? private void Demo7()
- ??????? {
- ??????????? Task < int > t1 = Task .Factory.StartNew< int >(() =>
- ??????????? {
- ??????????????? int total = 0;
- ??????????????? for ( int i = 0; i < 10; i++)
- ??????????????????? total += i;
- ??????????????? Thread .Sleep(12000);
- ??????????????? return total;
- ??????????? });
- ??????????? Task < int > t2 = Task .Factory.StartNew< int >(() =>
- ??????????? {
- ??????????????? int total = 0;
- ??????????????? for ( int i = 10; i < 20; i++)
- ??????????????????? total += i;
- ??????????????? Thread .Sleep(10000);
- ??????????????? return total;
- ??????????? });
- ??????????? Task tfinal = Task .Factory.ContinueWhenAny< int >(
- ???????????????????????? new Task < int >[] { t1, t2 }, ( Task < int > task) =>
- ???????????????????????? {
- ???????????????????????????? if (task.Status == TaskStatus .RanToCompletion)
- ???????????????????????????? {
- ???????????????????????????????? Console .WriteLine(task.Result);
- ???????????????????????????? }
- ???????????????????????? });
- ??????????? Console .ReadLine();
- ??????? }
結論5:ContinueWhenAny和ContinueWhenAll對特定條件執行,還是有些用處的。
?
?
好了,這篇文章算是完結了,4.0中的并行和線程確實簡單了很多,使用起來也很方便,為了性能的提升還是要適當的使用下。
謝謝觀賞
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
