日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

26计算限制的异步操作01-CLR

發(fā)布時(shí)間:2023/12/19 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 26计算限制的异步操作01-CLR 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

由CLR via C#(第三版) ,摘抄記錄...

  異步優(yōu)點(diǎn):在GUI應(yīng)用程序中保持UI可響應(yīng)性,以及多個(gè)CPU縮短一個(gè)耗時(shí)計(jì)算所需的時(shí)間。

1、CLR線程池基礎(chǔ):為提高性能,CLR包含了代碼來(lái)管理他自己的線程池--線程的集合。每CLR一個(gè)線程池,這個(gè)線程池就由CLR控制的所有appDomain共享。如果你進(jìn)程中有多個(gè)CLR,就有多個(gè)線程池。

  CLR初始化時(shí),池空,線程池維護(hù)一個(gè)操作請(qǐng)求隊(duì)列。應(yīng)用調(diào)用方法執(zhí)行異步,將一個(gè)記錄項(xiàng)(entry)追加到線程池的隊(duì)列。線程池從隊(duì)列提取記錄項(xiàng),派遣(dispatch)給一個(gè)線程池線程,如沒(méi)有,則創(chuàng)建一個(gè)新線程。完成任務(wù)后線程不銷毀,在線程池空閑等待響應(yīng)另一個(gè)請(qǐng)求,這樣提高性能。 當(dāng)請(qǐng)求速度超過(guò)處理速度,就會(huì)創(chuàng)建額外線程。如果請(qǐng)求停止,線程空閑一段時(shí)間后,會(huì)自己醒來(lái)終止自己以釋放資源。 線程池是啟發(fā)式的,由任務(wù)多少,和可用CPU的多少,創(chuàng)建線程

  在內(nèi)部,線程池將自己的線程分為 工作者(Worker)線程和I/0線程

2、簡(jiǎn)單的計(jì)算限制操作

  將一個(gè)異步的、計(jì)算限制的操作放到一個(gè)線程池的隊(duì)列中,通常可以調(diào)用ThreadPool類定義的以下方法之一:

  //將方法排入隊(duì)列以便執(zhí)行。此方法在有線程池線程變得可用時(shí)執(zhí)行。static Boolean QueueUserWorkItem(WaitCallback callBack);//將方法排入隊(duì)列以便執(zhí)行,并指定包含該方法所用數(shù)據(jù)的對(duì)象。此方法在有線程池線程變得可用時(shí)執(zhí)行。static Boolean QueueUserWorkItem(WaitCallback callBack,Object state);
~~~~
  模擬程序 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 Console.WriteLine("Main thread: queuing an asynchronous operation"); 6 ThreadPool.QueueUserWorkItem(ComputeBoundOp, 5); 7 Console.WriteLine("Main thread: Doing other work here..."); 8 Thread.Sleep(10000); // 模擬其它工作 (10 秒鐘) 9 //Console.ReadLine(); 10 } 11 12 // 這是一個(gè)回調(diào)方法,必須和WaitCallBack委托簽名一致 13 private static void ComputeBoundOp(Object state) 14 { 15 // 這個(gè)方法通過(guò)線程池中線程執(zhí)行 16 Console.WriteLine("In ComputeBoundOp: state={0}", state); 17 Thread.Sleep(1000); // 模擬其它工作 (1 秒鐘) 18 19 // 這個(gè)方法返回后,線程回到線程池,等待其他任務(wù) 20 } 21 } 線程池01

? ?如果回調(diào)方法有異常,CLR會(huì)終止進(jìn)程

? ?3、 執(zhí)行上下文 ? ?每個(gè)線程都關(guān)聯(lián)了一個(gè)執(zhí)行上下文數(shù)據(jù)結(jié)構(gòu)。執(zhí)行上下文(execution context)包括的東西有:

  • 安全設(shè)置:壓縮棧、Thread的Principal屬性[指示線程的調(diào)度優(yōu)先級(jí)]和Windows身份;
  • 宿主設(shè)置:參見System.Threading.HostExecutionContextManager[提供使公共語(yǔ)言運(yùn)行時(shí)宿主可以參與執(zhí)行上下文的流動(dòng)(或移植)的功能];
  • 邏輯調(diào)用上下文數(shù)據(jù):參見System.Runtime.Remoting.Messaging.CallContext[提供與執(zhí)行代碼路徑一起傳送的屬性集]的LogicalSetData[將一個(gè)給定對(duì)象存儲(chǔ)在邏輯調(diào)用上下文中并將該對(duì)象與指定名稱相關(guān)聯(lián)]和LogicalGetData[從邏輯調(diào)用上下文中檢索具有指定名稱的對(duì)象]。

線程執(zhí)行代碼時(shí),有的操作會(huì)受到線程的執(zhí)行上下文設(shè)置(尤其是安全設(shè)置)的影響。理想情況下,每當(dāng)一個(gè)線程(初始線程)使用另一個(gè)線程(輔助線程)執(zhí)行任務(wù)時(shí),前者的執(zhí)行上下文應(yīng)該"流動(dòng)"(復(fù)制)到輔助線程。這就確保輔助線程執(zhí)行的任何操作使用的都是相同的安全設(shè)置和宿主設(shè)置。還確保了初始線程的邏輯調(diào)用上下文可以在輔助線程中使用

默認(rèn)情況下,CLR自動(dòng)造成初始線程的執(zhí)行上下文會(huì)"流動(dòng)"(復(fù)制)到任何輔助線程。這就是將上下文信息傳輸?shù)捷o助線程,但這對(duì)損失性能,因?yàn)閳?zhí)行上下文中包含大量信息,而收集這些信息,再將這些信息復(fù)制到輔助線程,要耗費(fèi)不少時(shí)間。如果輔助線程又采用更多的輔助線程,還必須創(chuàng)建和初始化更多的執(zhí)行上下文數(shù)據(jù)結(jié)構(gòu)。

System.Threading命名空間中有一個(gè)ExecutionContext類[管理當(dāng)前線程的執(zhí)行上下文],它允許你控制線程的執(zhí)行上下文如何從一個(gè)線程"流動(dòng)"(復(fù)制)到另一個(gè)線程。下面展示了這個(gè)類的樣子:

1 public sealed class ExecutionContext : IDisposable, ISerializable 2 { 3 [SecurityCritical] 4 //取消執(zhí)行上下文在異步線程之間的流動(dòng) 5 public static AsyncFlowControl SuppressFlow(); 6 //恢復(fù)執(zhí)行上下文在異步線程之間的流動(dòng) 7 public static void RestoreFlow(); 8 //指示當(dāng)前是否取消了執(zhí)行上下文的流動(dòng)。 9 public static bool IsFlowSuppressed(); 10 11 //不常用方法沒(méi)有列出 12 } ExecutionContext

可用這個(gè)類阻止一個(gè)執(zhí)行上下文的流動(dòng),從而提升應(yīng)用程序的性能對(duì)于服務(wù)器應(yīng)用程序,性能的提升可能非常顯著。但是,客戶端應(yīng)用程序的性能提升不了多少。另外,由于SuppressFlow方法用[SecurityCritical]attribute進(jìn)行了標(biāo)識(shí),所以在某些客戶端應(yīng)用程序(比如Silverlight)中是無(wú)法調(diào)用的。當(dāng)然,只有在輔助線程不需要或者不防問(wèn)上下文信息時(shí),才應(yīng)該組織執(zhí)行上下文的流動(dòng)。如果初始線程的執(zhí)行上下文不流向輔助線程,輔助線程會(huì)使用和它關(guān)聯(lián)起來(lái)的任何執(zhí)行上下文。在這種情況下,輔助線程不應(yīng)該執(zhí)行要依賴于執(zhí)行上下文狀態(tài)(比如用戶的Windows身份)的代碼。

注意:添加到邏輯調(diào)用上下文的項(xiàng)必須是可序列化的。對(duì)于包含了邏輯調(diào)用上下文數(shù)據(jù)線的一個(gè)執(zhí)行上下文,如果讓它流動(dòng),可能嚴(yán)重?fù)p害性能,因?yàn)闉榱瞬蹲綀?zhí)行上下文,需對(duì)所有數(shù)據(jù)項(xiàng)進(jìn)行序列化和反序列化。

下例展示了向CLR的線程池隊(duì)列添加一個(gè)工作項(xiàng)的時(shí)候,如何通過(guò)阻止執(zhí)行上下文的流動(dòng)來(lái)影響線程邏輯調(diào)用上下文中的數(shù)據(jù):

1 static void Main(string[] args) 2 { 3 // 將一些數(shù)據(jù)放到Main線程的邏輯調(diào)用上下文中 4 CallContext.LogicalSetData("Name", "Jeffrey"); 5 6 // 線程池能訪問(wèn)到邏輯調(diào)用上下文數(shù)據(jù),加入到程序池隊(duì)列中 7 ThreadPool.QueueUserWorkItem( 8 state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name"))); 9 10 11 // 現(xiàn)在阻止Main線程的執(zhí)行上下文流動(dòng) 12 ExecutionContext.SuppressFlow(); 13 14 //再次訪問(wèn)邏輯調(diào)用上下文的數(shù)據(jù) 15 ThreadPool.QueueUserWorkItem( 16 state => Console.WriteLine("Name={0}", CallContext.LogicalGetData("Name"))); 17 18 //恢復(fù)Main線程的執(zhí)行上下文流動(dòng) 19 ExecutionContext.RestoreFlow(); 20 } 執(zhí)行上下文的阻止

會(huì)得到一下結(jié)果:
? Name=Jeffrey
? Name=

雖然現(xiàn)在我們討論的是調(diào)用ThreadPool.QueueUserWorkItem時(shí)阻止執(zhí)行上下文的流動(dòng),但在使用Task對(duì)象(參見26.5節(jié)”任務(wù)“),以及在發(fā)起異步I/O操作(參見第27章“I/o限制的異步操作”)時(shí),這個(gè)技術(shù)也會(huì)用到。

4、協(xié)作式取消 ?標(biāo)準(zhǔn)的取消模式,協(xié)作的,想取消的操作必須顯式地支持取消。為長(zhǎng)時(shí)間運(yùn)行的的計(jì)算限制操作添加取消能力。

  首先,先解釋一下FCL提供的兩個(gè)主要類型,它們是標(biāo)準(zhǔn)協(xié)作式取消模式的一部分。

為了取消一個(gè)操作,首先必須創(chuàng)建一個(gè)System.Thread.CancellationTokenSource[通知?CancellationToken,告知其應(yīng)被取消]對(duì)象。這個(gè)類如下所示: 1 public class CancellationTokenSource : IDisposable 2 { 3 //構(gòu)造函數(shù) 4 public CancellationTokenSource(); 5 //獲取是否已請(qǐng)求取消此 System.Threading.CancellationTokenSource 6 public bool IsCancellationRequested { get; } 7 //獲取與此 System.Threading.CancellationTokenSource 關(guān)聯(lián)的 System.Threading.CancellationToken 8 public CancellationToken Token; 9 //傳達(dá)取消請(qǐng)求。 10 public void Cancel(); 11 //傳達(dá)對(duì)取消的請(qǐng)求,并指定是否應(yīng)處理其余回調(diào)和可取消操作。 12 public void Cancel(bool throwOnFirstException); 13 ... 14 } CancellationTokenSource

這個(gè)對(duì)象包含了管理取消有關(guān)的所有狀態(tài)。構(gòu)造好一個(gè)CancellationTokenSource(引用類型)之后,可以從它的Token屬性獲得一個(gè)或多個(gè)CancellationToken(值類型)實(shí)例,并傳給你的操作,使那些操作可以取消。以下是CancellationToken值類型最有用的一些成員:

1 public struct CancellationToken //一個(gè)值類型 2 { 3 //獲取此標(biāo)記是否能處于已取消狀態(tài),IsCancellationRequested 由非通過(guò)Task來(lái)調(diào)用(invoke)的一個(gè)操作調(diào)用(call) 4 public bool IsCancellationRequested { get; } 5 //如果已請(qǐng)求取消此標(biāo)記,則引發(fā) System.OperationCanceledException,由通過(guò)Task來(lái)調(diào)用的操作調(diào)用 6 public void ThrowIfCancellationRequested(); 7 //獲取在取消標(biāo)記時(shí)處于有信號(hào)狀態(tài)的 System.Threading.WaitHandle,取消時(shí),WaitHandle會(huì)收到信號(hào) 8 public WaitHandle WaitHandle { get; } 9 //返回空 CancellationToken 值。 10 public static CancellationToken None 11 //注冊(cè)一個(gè)將在取消此 System.Threading.CancellationToken 時(shí)調(diào)用的委托。省略了簡(jiǎn)單重載版本 12 public CancellationTokenRegistration Register(Action<object> callback, object state, bool useSynchronizationContext); 13 14 //省略了GetHashCode、Equals成員 15 } CancellationToken

?CancellationToken實(shí)例是一個(gè)輕量級(jí)的值類型,它包含單個(gè)私有字段:對(duì)它的CancellationTokenSource對(duì)象的一個(gè)引用。在一個(gè)計(jì)算限制操作的循環(huán)中,可以定時(shí)調(diào)用CancellationToken的IsCancellationRequested屬性,了解循環(huán)是否應(yīng)該提前終止,進(jìn)而終止計(jì)算限制的操作。當(dāng)然,提前終止的好處在于,CPU不再需要把時(shí)間浪費(fèi)在你對(duì)其結(jié)果已經(jīng)不感興趣的一個(gè)操作上。現(xiàn)在,用一些示例代碼演示一下:

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 CancellationTokenSource cts = new CancellationTokenSource(); 6 7 // 將CancellationToken和"要循環(huán)到的目標(biāo)數(shù)"傳入操作中 8 ThreadPool.QueueUserWorkItem(o => Count(cts.Token, 1000)); 9 10 Console.WriteLine("Press <Enter> to cancel the operation."); 11 Console.ReadLine(); 12 cts.Cancel(); // 如果Count方法已返回,Cancel沒(méi)有任何效果 13 // Cancel立即返回,方法從這里繼續(xù)運(yùn)行 14 15 Console.ReadLine(); 16 } 17 18 private static void Count(CancellationToken token, Int32 countTo) 19 { 20 for (Int32 count = 0; count < countTo; count++) 21 { 22 //判斷是否接收到了取消任務(wù)的信號(hào) 23 if (token.IsCancellationRequested) 24 { 25 Console.WriteLine("Count is cancelled"); 26 break; // 退出循環(huán)以停止操作 27 } 28 29 Console.WriteLine(count); 30 Thread.Sleep(200); // 出于演示浪費(fèi)一點(diǎn)時(shí)間 31 } 32 Console.WriteLine("Count is done"); 33 } 34 } 取消示例 注意:如果要執(zhí)行一個(gè)操作,并禁止取消它,可以向該操作傳遞通過(guò)調(diào)用CancellationToken的靜態(tài)None屬性返回的CancellationToken。 如果愿意,可以登記一個(gè)或多個(gè)方法,在取消一個(gè)CancellationTokenSource時(shí)調(diào)用。每個(gè)回調(diào)方法都用CancellactionToken的Register方法來(lái)登記的。要向這個(gè)方法傳遞一個(gè)Action<Object>委托;一個(gè)要通過(guò)委托傳給回調(diào)的狀態(tài);以及一個(gè)Boolean值(名為useSynchronizationContext),該值指定了是否要使用調(diào)用線程的SynchronizationContext來(lái)調(diào)用委托。如果為useSynchronizationContext參數(shù)傳遞的是false,那么調(diào)用Cancel的線程會(huì)順序調(diào)用已登記的所有方法。如果為true,那么回調(diào)會(huì)被send(而不是post)給已捕捉的SynchronizationContext對(duì)象,后者決定由哪個(gè)線程調(diào)用回調(diào)方法。 說(shuō)明:如果執(zhí)行send操作,要等到目標(biāo)線程那里處理完畢之后才會(huì)返回。再次期間,調(diào)用線程會(huì)被阻塞。這相當(dāng)于同步調(diào)用。而如果執(zhí)行post操作,是指將東西post到一個(gè)隊(duì)列中便完事,調(diào)用線程可以立即返回。相當(dāng)于異步調(diào)用。以后會(huì)詳細(xì)提到。 如果多次調(diào)用Regiser,那么多個(gè)回調(diào)方法都會(huì)調(diào)用。這些回調(diào)方法可能拋出未處理的異常。如果調(diào)用CancellationTokenSource的Cancel方法,向它傳遞true,那么拋出了未處理異常的第一個(gè)回調(diào)方法會(huì)組織其他回調(diào)方法的執(zhí)行,拋出的異常也會(huì)從Cancel中拋出。如果調(diào)用Cancel并向它傳遞false,那么登記的所有回調(diào)方法都會(huì)調(diào)用。所有未處理的異常都會(huì)添加到一個(gè)集合中。所有回調(diào)方法都執(zhí)行后,如果其中任何一個(gè)拋出一個(gè)未處理的異常,Cancel就會(huì)排除一個(gè)AggregateException,該異常實(shí)例的InnerException屬性會(huì)被設(shè)為以拋出的所有異常對(duì)象的一個(gè)集合。如果以等級(jí)的所有回調(diào)方法都沒(méi)有拋出異常,那么Cancel直接返回,不拋出任何異常。 注:沒(méi)辦法把InnerExceptions的一個(gè)異常與特定操作對(duì)應(yīng)起來(lái)。需查看StackTrace屬性并手動(dòng)檢查代碼。 CancellactionToken的Register方法返回一個(gè)CancellationTokenRegistration,如下所示: public struct CancellationTokenRegistration : IEquatable<CancellationTokenRegistration>, IDisposable {public void Dispose();....... }

  可調(diào)用Dispose從關(guān)聯(lián)的CancellationTokenSource中刪除一個(gè)已登記的回調(diào);這樣一來(lái),在調(diào)用Cancel時(shí),便不會(huì)再調(diào)用這個(gè)回調(diào)。以下代碼演示了如何向一個(gè)CancellationTokenSource登記兩個(gè)回調(diào):

private static void Register() {var cts = new CancellationTokenSource();cts.Token.Register(() => Console.WriteLine("Canceled 1"));cts.Token.Register(() => Console.WriteLine("Canceled 2"));// 出于測(cè)試目的,讓我們?nèi)∠?#xff0c;以便執(zhí)行兩個(gè)回調(diào)cts.Cancel(); }

  可通過(guò)鏈接另一組的CancellationTokenSource來(lái)新建一個(gè)CancellationTokenSource對(duì)象。任何一個(gè)鏈接的CancellationTokenSource被取消,這個(gè)CancellationTokenSource對(duì)象就會(huì)被取消。以下代碼對(duì)此進(jìn)行的演示:

1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 創(chuàng)建一個(gè) CancellationTokenSource 6 var cts1 = new CancellationTokenSource(); 7 cts1.Token.Register(() => Console.WriteLine("cts1 canceled")); 8 9 // 創(chuàng)建另一個(gè) CancellationTokenSource 10 var cts2 = new CancellationTokenSource(); 11 cts2.Token.Register(() => Console.WriteLine("cts2 canceled")); 12 13 // 創(chuàng)建新的CancellationTokenSource,它在 cts1 o或 ct2 is 取消時(shí)取消 14 var ctsLinked = CancellationTokenSource.CreateLinkedTokenSource(cts1.Token, cts2.Token); 15 ctsLinked.Token.Register(() => Console.WriteLine("linkedCts canceled")); 16 17 // 取消其中一個(gè) CancellationTokenSource objects (這里選擇了 cts2) 18 cts2.Cancel(); 19 20 // 顯示哪個(gè) CancellationTokenSource objects 被取消 了 21 Console.WriteLine("cts1 canceled={0}, cts2 canceled={1}, ctsLinked canceled ={2}", 22 cts1.IsCancellationRequested, cts2.IsCancellationRequested, ctsLinked.IsCancellationRequested); 23 24 Console.ReadLine(); 25 } 26 27 } 鏈接式的取消

?

5、任務(wù)?調(diào)用ThreadPool的QueueUserWorkItem方法來(lái)發(fā)起一次異步的受計(jì)算限制的操作是非常簡(jiǎn)單的。然而。這個(gè)技術(shù)存在許多限制。最大的問(wèn)題是沒(méi)有一個(gè)內(nèi)建的機(jī)制讓你知道操作在什么時(shí)候完成,也沒(méi)有一個(gè)機(jī)制在操作完成時(shí)獲得一個(gè)返回值。為了克服這些限制并解決一些其它問(wèn)題,Microsoft引入了任務(wù)(task)的概念。我們通過(guò)System.Treading.Tasks命名空間中的類型來(lái)使用它們。

現(xiàn)在,我們可以使用任務(wù)來(lái)做相同的事情: ThreadPool.QueueUserWorkItem(ComputeBoundOp,5) // 調(diào)用QueueUserWorkItemnew Task(ComputeBoundOp,5).Start(); // 用Task來(lái)做相同的事情 在上述代碼中,創(chuàng)建了Task對(duì)象,并立即調(diào)用Start方法來(lái)調(diào)度該任務(wù)方法。當(dāng)然,也可以先創(chuàng)建好Task對(duì)象,以后在調(diào)用Start方法。把Task對(duì)象傳遞給一個(gè)方法,由后者決定執(zhí)行時(shí)間和頻率。 創(chuàng)建一個(gè)Task的方式總是調(diào)用構(gòu)造器,傳遞一個(gè)Action或者Action<Object>委托。這個(gè)委托就是你想執(zhí)行的操作。如果傳遞期待一個(gè)Object的方法,還必須向Task的構(gòu)造器傳遞最終想傳給操作的實(shí)參。還可以選擇向Task的構(gòu)造器傳遞一個(gè)CancellationToken,這便允許Task在調(diào)度之前取消。 還可以選擇向構(gòu)造器傳遞一些TaskCreationOptions標(biāo)志來(lái)控制Task的執(zhí)行方式。TaskCreationOptions是一個(gè)枚舉類型,定義了一組可按位OR到一起的標(biāo)志。它的定義如下: 1 [FlagsAttribute, SerializableAttribute] 2 public enum TaskCreationOptions 3 { 4 //指定應(yīng)使用默認(rèn)行為 5 None = 0x0, 6 //提示 TaskScheduler 以一種盡可能公平的方式安排任務(wù),這意味著較早安排的任務(wù)將更可能較早運(yùn)行,而較晚安排運(yùn)行的任務(wù)將更可能較晚運(yùn)行。造成默認(rèn)的TaskScheduler(任務(wù)調(diào)度器) 將線程池中的任務(wù)放到全局隊(duì)列中,而不是放到一個(gè)工作者線程的本地隊(duì)列中 7 PreferFairness = 0x1, 8 //指定某個(gè)任務(wù)將是運(yùn)行時(shí)間長(zhǎng)、粗粒度的操作。 它會(huì)給TaskScheduler一個(gè)提議,告訴它線程可能要“長(zhǎng)時(shí)間運(yùn)行”,將由TaskScheduler 決定如何解析還這個(gè)提示。 9 LongRunning = 0x2, 10 //將一個(gè)任務(wù)和它的父Task關(guān)聯(lián)。 11 AttachedToParent = 0x4, 12 #if NET_4_5 13 // 14 DenyChildAttach = 0x8, 15 HideScheduler = 0x10 16 #endif 17 } TaskCreationOptions

大多是標(biāo)志只是一些提議而已,TaskScheduler在調(diào)度一個(gè)Task時(shí),可能會(huì)也可能不會(huì)采納這些提議。不過(guò),AttacedToParent標(biāo)志總是得到采納,因?yàn)樗蚑askScheduler本身無(wú)關(guān)。

? ?

  5.1 等待任務(wù)完成并獲取它的結(jié)果

對(duì)于任務(wù),可以等待它完成,然后獲取它們的結(jié)果。假定有一個(gè)Sum方法,在n值很大的時(shí)候,它要執(zhí)行較長(zhǎng)的時(shí)間: private static Int32 Sum(Int32 n) {Int32 sum = 0;for (; n > 0; n--) checked { sum += n; } //如果n太大,這一行代碼會(huì)拋出異常return sum;} 現(xiàn)在可以構(gòu)造一個(gè)Task<TResult>對(duì)象(派生自Task),并為泛型TResult參數(shù)傳遞計(jì)算限制操作的返回類型。在開始任務(wù)后,可以等待它完成并獲取結(jié)果,如以下代碼所示: 1 class Program 2 { 3 static void Main(string[] args) 4 { 5 // 創(chuàng)建 Task, 推遲啟動(dòng)它 6 Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10000); 7 8 // 可以在以后某個(gè)時(shí)間啟動(dòng)任務(wù) 9 t.Start(); 10 11 // 可以選擇顯式的等待任務(wù)完成 12 t.Wait(); 13 14 Console.WriteLine("The sum is: " + t.Result); //一個(gè)Int32的值 15 Console.ReadLine(); 16 } 17 18 private static Int32 Sum(Int32 n) 19 { 20 Int32 sum = 0; 21 for (; n > 0; n--) checked { sum += n; } //如果n太大,這一行代碼會(huì)拋出異常 22 return sum; 23 } 24 25 } Task01 如果計(jì)算限制的任務(wù)拋出一個(gè)未處理的異常,這個(gè)一樣會(huì)被"侵吞"并存儲(chǔ)到一個(gè)集合中,而線程池線程允許返回到線程池中。調(diào)用Wait方法或者Result屬性時(shí),這些成員會(huì)拋出一個(gè)System.AggregateException對(duì)象。 提示:一個(gè)線程調(diào)用Wait方法時(shí),系統(tǒng)會(huì)檢查系統(tǒng)要等待的Task是否已開始執(zhí)行。如果是,調(diào)用Wait的線程會(huì)阻塞,直到Task運(yùn)行結(jié)束為止。但是,如果Task還沒(méi)有開始執(zhí)行,系統(tǒng)可能(取決于TaskSecheduler)使用調(diào)用Wait的線程來(lái)執(zhí)行Task。如果發(fā)生這種情況,那么調(diào)用Wait的線程不會(huì)阻塞;它會(huì)執(zhí)行Task并立即返回。這樣做的好處在于,沒(méi)有線程會(huì)被阻塞,所以減少了資源的使用(因?yàn)椴恍枰獎(jiǎng)?chuàng)建一個(gè)線程來(lái)替代被阻塞的線程),并提升了性能(因?yàn)椴恍枰〞r(shí)間創(chuàng)建一個(gè)線程,也沒(méi)有上下文切換)。但是不好的地方在于,加入線程在調(diào)用Wait前已經(jīng)獲得了一個(gè)線程同步鎖,而Task試圖獲取同一個(gè)鎖,就會(huì)造成一個(gè)死鎖的線程。 AggregateException類型用于封閉異常對(duì)象的一個(gè)集合(如果父任務(wù)生成了多個(gè)字任務(wù),而多個(gè)子任務(wù)都拋出異常,這個(gè)集合便有可能包含多個(gè)異常)。該類型有一個(gè)InnerExceptions屬性,它返回一個(gè)ReadOnlyCollection<Excepyion>對(duì)象。不要混淆InnerException和InnerException屬性,后者是AggregateException類從System.Exception基類繼承來(lái)的。對(duì)于上例來(lái)說(shuō),AggregateException的InnerExceptions屬性的元素0將引用由計(jì)算限制方法(Sum)拋出的實(shí)際System.OverflowException對(duì)象。 為方便編碼,AggregateException重寫了Exception的GetBaseException方法。AggregateException的這個(gè)實(shí)現(xiàn)會(huì)返回作為問(wèn)題根源的最內(nèi)層的AggregateException。AggregateException還提供了一個(gè)Flatten方法,它創(chuàng)建一個(gè)新的AggregateException,其InnerExceptions屬性包含一個(gè)異常列表,其中的異常是通過(guò)遍歷原始AggregateException的內(nèi)層異常層次結(jié)構(gòu)而生成的。最后,AggregateException還提供了一個(gè)Handle方法,它為AggregateException中包含的每個(gè)異常都調(diào)用一個(gè)回調(diào)方法,然后,回調(diào)方法可以為每個(gè)在調(diào)用Handle之后,如果至少有一個(gè)異常沒(méi)有處理,就創(chuàng)建一個(gè)新的AggregateException對(duì)象,其中只包含未處理的異常,并拋出這個(gè)新的AggregateException對(duì)象。

? ?  Task還提供等待任務(wù)數(shù)組。WaitAny是任意任務(wù)完成就返回,不再阻塞。反饋的是完成任務(wù)在數(shù)組中的下標(biāo),若超時(shí)則返回-1

   ?類似的,Task類還提供了靜態(tài)WaitAll方法,它阻塞調(diào)用線程,直到數(shù)組中所有的Task對(duì)象都完成如果Task對(duì)象都完成,WaitAll方法返回true。如果?發(fā)生超時(shí),就返回false

    如果WaitAnyWaitAll通過(guò)一個(gè)CancellationToken而取消,會(huì)拋出一個(gè)OpreationCanceledException。

    如果一直不調(diào)用 Wait獲Result,或者一直不查詢Task的Exception,就可能忽略一些異常,當(dāng)Task對(duì)象的Finalize執(zhí)行時(shí),會(huì)拋出異常,這是CLR終結(jié)器拋出的,不能捕捉,進(jìn)程會(huì)立即終止。(CLR第三版的 26.5.1章節(jié)提到一種處理,見P648)

5.2取消任務(wù)??可以用一個(gè)CancellationTokenSource取消一個(gè)Task.

private static Int32 Sum(CancellationToken ct, Int32 n){Int32 sum = 0;for (; n > 0; n--){// 在取消標(biāo)志引用的CancellationTokenSource上如果調(diào)用Cancel,// 下面這一行就拋出OpreationCanceledExceptionct.ThrowIfCancellationRequested();checked { sum += n; } //如果n太大,這一行代碼會(huì)拋出異常}return sum;}

  

在上述代碼中,在計(jì)算限制的循環(huán)中,我們通過(guò)調(diào)用CancellationToken的ThrowIfCancellationRequested方法來(lái)定時(shí)檢查操作是否已取消。這個(gè)方法和CancellationToken的IsCancellationRequested屬性相似。如果CancellationTokenSource已經(jīng)取消,ThrowIfCancellationRequested會(huì)拋出一個(gè)OpreationCanceledException。之所以選擇拋出一個(gè)異常,是因?yàn)橛袆e于ThreadPool的QueueUserWorkItem方法所初始化的工作項(xiàng)(work item),任務(wù)有表示已經(jīng)完成的方法,甚至還有一個(gè)返回值。所以,需要采取一種方式將已完成的任務(wù)和出錯(cuò)的任務(wù)區(qū)分開。而讓任務(wù)拋出一個(gè)異常,就可以知道任務(wù)沒(méi)有一直運(yùn)行到結(jié)束。 現(xiàn)在,我們像下面這樣創(chuàng)建CancellationTokenSource和Task對(duì)象: 1 static void Main(string[] args) 2 { 3 CancellationTokenSource cts = new CancellationTokenSource(); 4 Task<Int32> t = new Task<Int32>(() => Sum(cts.Token, 10000), cts.Token); 5 6 t.Start(); 7 8 // 在之后的某個(gè)時(shí)間,取消CancellationTokenSource以取消Task 9 cts.Cancel(); 10 11 try 12 { 13 // 如果任務(wù)已經(jīng)取消,Result會(huì)拋出一個(gè)AggregateException 14 Console.WriteLine("The sum is: " + t.Result); // An Int32 value 15 } 16 catch (AggregateException ae) 17 { 18 // 將任何OperationCanceledException對(duì)象都視為已處理 19 // 其他任何異常都造成拋出一個(gè)新的AggregateException,其中 20 // 只包含未處理的異常 21 ae.Handle(e => e is OperationCanceledException); 22 23 // 所有的異常都處理好之后,執(zhí)行下面這一行 24 Console.WriteLine("Sum was canceled"); 25 } 26 Console.ReadLine(); 27 } 可取消Task示例 創(chuàng)建一個(gè)Task時(shí),可以將一個(gè)CancellationToken傳給Task的構(gòu)造器,從而將這個(gè)CancellationToken和該Task關(guān)聯(lián)起來(lái)。如果CancellationToken在Task調(diào)度前取消,Task會(huì)被取消,永遠(yuǎn)不會(huì)執(zhí)行。但是,如果Task已經(jīng)調(diào)度,那么Task為了允許它的操作在執(zhí)行期間取消,Task的代碼必須顯式支持取消。遺憾的是,雖然Task對(duì)象關(guān)聯(lián)了一個(gè)CancellationToken,但沒(méi)有辦法訪問(wèn)它。因此,必須通過(guò)某種方式,在Task的代碼本身中獲得用于創(chuàng)建Task對(duì)象的同一個(gè)CancellationToken。為了寫這樣的代碼,最簡(jiǎn)單的方法就是使用一個(gè)lambda表達(dá)式,并將CancellationToken作為一個(gè)閉包變量"傳遞"(就像上例所示)。 5.3?一個(gè)任務(wù)完成時(shí)自動(dòng)啟動(dòng)一個(gè)新任務(wù)

? ?? 要寫可伸縮的軟件,一定不能使你的線程阻塞。這意味著如果調(diào)用Wait,或者在任何尚未完成時(shí)查詢?nèi)蝿?wù)的Result屬性(Result內(nèi)部會(huì)調(diào)用Wait),極有可能造成線程池創(chuàng)建一個(gè)新線程,這增大了資源的消耗,并損害了伸縮性。幸好,有更好的方式知道一個(gè)任務(wù)在上面時(shí)候結(jié)束運(yùn)行。一個(gè)任務(wù)完成時(shí),它可以啟動(dòng)另一個(gè)任務(wù)。下面重寫了前面的代碼,它不會(huì)阻塞線程:

? ??

static void Main(string[] args){// 創(chuàng)建 Task, 推遲啟動(dòng)它, 繼續(xù)另一個(gè)任務(wù)Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10000);// 可以在以后某個(gè)時(shí)間啟動(dòng)任務(wù)t.Start();// ContinueWith 返回一個(gè) Task 但一般不再關(guān)心這個(gè)對(duì)象Task cwt = t.ContinueWith(task => Console.WriteLine("The sum is: " + task.Result));cwt.Wait();Console.ReadLine();}

  

現(xiàn)在,當(dāng)執(zhí)行Sum的任務(wù)完成后,這個(gè)任務(wù)會(huì)啟動(dòng)另一個(gè)任務(wù)(也在某個(gè)線程池線程上)以顯示結(jié)果。執(zhí)行上述代碼的線程不會(huì)進(jìn)入阻塞狀態(tài)并等待這個(gè)兩個(gè)任務(wù)中的任何一個(gè)完成。相反,線程可以執(zhí)行其它代碼。如果線程線程本身就是線程池線程,它可以返回到池中,以執(zhí)行其他操作。注意,執(zhí)行Sum的任務(wù)可能在調(diào)用ContinueWith之前完成。但這不是一個(gè)問(wèn)題,因?yàn)镃ontinueWith方法會(huì)看到Sum任務(wù)已經(jīng)完成,會(huì)立即啟動(dòng)顯示結(jié)果的任務(wù)。 另外,注意ContinueWith會(huì)返回新的Task對(duì)象的一個(gè)引用。當(dāng)然,可以用這個(gè)Task對(duì)象調(diào)用各種成員(比如Wait,Result,甚至ContinueWith),但你一般都是忽略這個(gè)Task對(duì)象,不把它的引用保存到一個(gè)變量中。 Task對(duì)象內(nèi)部包含了Continue任務(wù)的一個(gè)集合。所以,實(shí)際上可以用一個(gè)Task對(duì)象來(lái)多次調(diào)用ContinueWith。任務(wù)完成時(shí),所有ContinueWith任務(wù)都會(huì)進(jìn)入線程池的隊(duì)列中。此外,調(diào)用ContinueWith時(shí),可以傳遞對(duì)一組TaskContinuationOptions枚舉值進(jìn)行按位OR運(yùn)行的結(jié)果。前4個(gè)標(biāo)志(None,PreferFairness,LongRunning和AttachToParent)與早先描述的TaskCreationOptions枚舉類型提供的標(biāo)志完全一致,下面是TaskContinuationOptions類型的定義: 1 [System.FlagsAttribute, System.SerializableAttribute] 2 public enum TaskContinuationOptions 3 { 4 None = 0x00000, 5 PreferFairness = 0x00001, 6 LongRunning = 0x00002, 7 AttachedToParent = 0x00004, 8 #if NET_4_5 9 DenyChildAttach = 0x00008, 10 HideScheduler = 0x00010, 11 LazyCancellation = 0x00020, 12 #endif 13 //指定不應(yīng)在延續(xù)任務(wù)前面的任務(wù)已完成運(yùn)行的情況下安排延續(xù)任務(wù)。 此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 14 NotOnRanToCompletion = 0x10000, 15 //指定不應(yīng)在延續(xù)任務(wù)前面的任務(wù)引發(fā)了未處理異常的情況下安排延續(xù)任務(wù)。 此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 16 NotOnFaulted = 0x20000, 17 //指定不應(yīng)在延續(xù)任務(wù)前面的任務(wù)已取消的情況下安排延續(xù)任務(wù)。 此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 18 NotOnCanceled = 0x40000, 19 //指定只應(yīng)在延續(xù)任務(wù)前面的任務(wù)已完成運(yùn)行的情況下才安排延續(xù)任務(wù)。 此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 20 OnlyOnRanToCompletion = 0x60000, 21 //指定只有在延續(xù)任務(wù)前面的任務(wù)引發(fā)了未處理異常的情況下才應(yīng)安排延續(xù)任務(wù)。 此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 22 OnlyOnFaulted = 0x50000, 23 //指定只應(yīng)在延續(xù)任務(wù)前面的任務(wù)已取消的情況下安排延續(xù)任務(wù)。此選項(xiàng)對(duì)多任務(wù)延續(xù)無(wú)效。 24 OnlyOnCanceled = 0x30000, 25 //指定應(yīng)同步執(zhí)行延續(xù)任務(wù)。 指定此選項(xiàng)后,延續(xù)任務(wù)將在導(dǎo)致前面的任務(wù)轉(zhuǎn)換為其最終狀態(tài)的相同線程上運(yùn)行。 如果在創(chuàng)建延續(xù)任務(wù)時(shí)已經(jīng)完成前面的任務(wù),則延續(xù)任務(wù)將在創(chuàng)建此延續(xù)任務(wù)的線程上運(yùn)行。 只應(yīng)同步執(zhí)行運(yùn)行時(shí)間非常短的延續(xù)任務(wù)。 26 ExecuteSynchronously = 0x80000, 27 } TaskContinuationOptions

調(diào)用COntinueWith時(shí),可以指定你希望新任務(wù)只有在第一個(gè)任務(wù)被取消時(shí)才運(yùn)行,這時(shí)使用TaskContinuationOptions.?OnlyOnCanceled標(biāo)志來(lái)實(shí)現(xiàn)。默認(rèn)情況下,如果沒(méi)有指定上述任何標(biāo)志,新任務(wù)無(wú)論如何都會(huì)執(zhí)行下去,不管第一個(gè)任務(wù)是如何完成的。一個(gè)Task完成時(shí),它的所有尚未運(yùn)行的延續(xù)任務(wù)都會(huì)自動(dòng)取消。下面用一個(gè)例子演示所有這些概念。

1 static void Main(string[] args) 2 { 3 Task<Int32> t = new Task<Int32>(n => Sum((Int32)n), 10000); 4 5 t.Start(); 6 7 // 每個(gè) ContinueWith 都返回一個(gè) Task,但你不必關(guān)心這些Task對(duì)象 8 t.ContinueWith(task => Console.WriteLine("The sum is: " + task.Result), 9 TaskContinuationOptions.OnlyOnRanToCompletion); 10 t.ContinueWith(task => Console.WriteLine("Sum threw: " + task.Exception), 11 TaskContinuationOptions.OnlyOnFaulted); 12 t.ContinueWith(task => Console.WriteLine("Sum was canceled"), 13 TaskContinuationOptions.OnlyOnCanceled); 14 15 Console.ReadLine(); 16 17 }

?5.4任務(wù)啟動(dòng)子任務(wù) ??

最后,任務(wù)支持父/子關(guān)系,如下代碼所示: static void Main(string[] args){Task<Int32[]> parent = new Task<Int32[]>(() =>{var results = new Int32[3]; // 創(chuàng)建數(shù)組來(lái)存儲(chǔ)結(jié)果// 這個(gè)任務(wù)創(chuàng)建并啟用了3個(gè)子任務(wù)new Task(() => results[0] = Sum(10000), TaskCreationOptions.AttachedToParent).Start();new Task(() => results[1] = Sum(20000), TaskCreationOptions.AttachedToParent).Start();new Task(() => results[2] = Sum(30000), TaskCreationOptions.AttachedToParent).Start();// 返回對(duì)數(shù)組的一個(gè)引用(即使數(shù)組元素可能還沒(méi)有初始化)return results;});var cwt = parent.ContinueWith(parentTask => Array.ForEach(parentTask.Result, Console.WriteLine));parent.Start();Console.ReadLine();} 在前面例子中,父任務(wù)創(chuàng)建并啟用3個(gè)Task對(duì)象。默認(rèn)情況下,一個(gè)任務(wù)創(chuàng)建的Task對(duì)象是頂級(jí)任務(wù),這些任務(wù)與創(chuàng)建它們的那個(gè)任務(wù)無(wú)關(guān)。然而,TaskContinuationOptions.?AttachedToParent 標(biāo)志將一個(gè)Task和創(chuàng)建它的那個(gè)Task關(guān)聯(lián)起來(lái),結(jié)果是除非所有子任務(wù)結(jié)束運(yùn)行,否則創(chuàng)建任務(wù)(父任務(wù))不會(huì)認(rèn)為已經(jīng)結(jié)束。調(diào)用ContinueWith方法創(chuàng)建一個(gè)Task時(shí),可以指定TaskContinuationOptions.?AttachedToParent 標(biāo)志將延續(xù)任務(wù)指定的一個(gè)子任務(wù)。 5.5 任務(wù)內(nèi)部解密? 每個(gè)Task對(duì)象都有一組構(gòu)成任務(wù)狀態(tài)的字段。有一個(gè)Int32ID、代表Task執(zhí)行狀態(tài)的一個(gè)Int32、對(duì)父任務(wù)的一個(gè)引用、對(duì)Task創(chuàng)建時(shí)指定的TaskScheduler的一個(gè)引用、對(duì)回調(diào)方法的一個(gè)引用、對(duì)要傳給回調(diào)方法的對(duì)象的一個(gè)引用(可通過(guò)Task的只讀AsynState屬性查詢)、對(duì)一個(gè)ExecutionContext的引用以及對(duì)一個(gè)ManualResetEventSlim對(duì)象的引用。除此之外,每個(gè)Task對(duì)象都有對(duì)根據(jù)需要?jiǎng)?chuàng)建一個(gè)一些補(bǔ)充狀態(tài)的一個(gè)引用。 在補(bǔ)充狀態(tài)中,包含一個(gè)CancellactionToken、一個(gè)ContinueWithTask對(duì)象集合、為拋出了未處理異常的子任務(wù)而準(zhǔn)備的一個(gè)Task對(duì)象集合等。雖然任務(wù)提供了大量功能,但并非是沒(méi)有代價(jià)的。因?yàn)楸仨殲樗械倪@些狀態(tài)分配內(nèi)存。如果不需要任務(wù)提供的附加功能,那么使用ThreadPool.QueueUserWorkItem,資源的使用效率上會(huì)更高一些。 Task和Task<TResult>類實(shí)現(xiàn)了IDisposable接口,允許你在用完Task對(duì)象后調(diào)用Dispose。如今,所有Dispose方法所做的都是關(guān)閉ManuaResetEventSlim對(duì)象。然而,可以定義從Task和Task<Result>派生的類,在這些類中分配它們自己的資源,并在它們重寫的Dispose方法中釋放這些資源。當(dāng)然,大多數(shù)開發(fā)人員都不會(huì)在自己的代碼中顯式的為一個(gè)Task對(duì)象調(diào)用Dispose;他們只讓垃圾回收器回收任何不再需要的資源。 在每個(gè)Yask對(duì)象中,都包含代表Task唯一ID的一個(gè)Int32字段。創(chuàng)建一個(gè)Task對(duì)象時(shí),字段會(huì)被初始化為零。第一次查詢Task的只讀ID屬性,屬性將一個(gè)唯一Int32值分配給該字段,并從屬性中返回它。TaskID從1開始,每分配一個(gè)ID都會(huì)遞增1.在Visual Studio調(diào)試器中查看一個(gè)Task對(duì)象,會(huì)造成調(diào)試器顯示Task的ID,從而造成為Task分配一個(gè)ID。 這個(gè)ID的意義在于,每個(gè)Task都可以用一個(gè)唯一的值來(lái)標(biāo)識(shí)。事實(shí)上,Visual Studio會(huì)在它的"并行任務(wù)"和"并行堆棧"窗口中會(huì)顯示這個(gè)任務(wù)ID。但是,由于不在自己的代碼中分配ID,所以幾乎不可能將這個(gè)ID和代碼正在做的事聯(lián)系起來(lái)。運(yùn)行一個(gè)任務(wù)的代碼時(shí),可以查詢Task的靜態(tài)CurrenId屬性,它返回一個(gè)可空的Int32(Int32?)。還可以在調(diào)式期間,在Vasul Studio的"監(jiān)視"或"即時(shí)"窗口中調(diào)用它,以便獲得當(dāng)前正在調(diào)試的代碼的ID。然后,可以在"并行任務(wù)"和"并行堆棧"窗口中找到自己的任務(wù)。如果當(dāng)前沒(méi)有任務(wù)正在執(zhí)行,查詢CurrenId屬性會(huì)返回null。 一個(gè)Task對(duì)象存在期間,可查詢Task的只讀Status屬性了解它在其生存期的什么位置。這個(gè)屬性返回一個(gè)TaskStatus值,定義如下: public enum TaskStatus{//這些標(biāo)志指出了一個(gè)Task在其生命周期內(nèi)的狀態(tài)// 任務(wù)已顯式創(chuàng)建,可以手動(dòng)Start()這個(gè)任務(wù)Created,// 任務(wù)已隱式創(chuàng)建,會(huì)自動(dòng)開始WaitingForActivation,// 任務(wù)已調(diào)度,但尚未運(yùn)行WaitingToRun,// 任務(wù)正在運(yùn)行Running,// 任務(wù)正在等待它的子任務(wù)完成,子任務(wù)完成后它才完成WaitingForChildrenToComplete,// 一個(gè)任務(wù)的最終狀態(tài)是以下三種之一// 已成功完成執(zhí)行的任務(wù)RanToCompletion,// 該任務(wù)已通過(guò)對(duì)其自身的 CancellationToken 引發(fā) OperationCanceledException 對(duì)取消進(jìn)行了確認(rèn),此時(shí)該標(biāo)記處于已發(fā)送信號(hào)狀態(tài);或者在該任務(wù)開始執(zhí)行之前,已向該任務(wù)的 CancellationToken 發(fā)出了信號(hào)Canceled,// 由于未處理異常的原因而完成的任務(wù)Faulted} 首先構(gòu)造一個(gè)Task對(duì)象時(shí),它的狀態(tài)是Created。以后,任務(wù)啟動(dòng)時(shí),它的狀態(tài)變?yōu)閃aitngToRun。Task在一個(gè)線程上運(yùn)行時(shí),它的狀態(tài)就變成了Running。任務(wù)停止運(yùn)行,并等待它的任何子任務(wù)時(shí),狀態(tài)變成WaitingForChildrenToComplete。任務(wù)完全結(jié)束時(shí),它會(huì)進(jìn)入以下三種狀態(tài)的一種:RanToCompletion、Canceled或Faulted。一個(gè)Task<Result>運(yùn)行完成時(shí),可通過(guò)Task<TResult>的Result屬性來(lái)查詢?nèi)蝿?wù)的結(jié)果。一個(gè)Task或者Task<TResult>出錯(cuò)時(shí),可以查詢Task的Exception屬性來(lái)獲得任務(wù)拋出的未處理的異常:該屬性總是返回一個(gè)AggregateException對(duì)象,它包含了所有未處理的異常。 為簡(jiǎn)化編碼,Task提供了幾個(gè)只讀的Boolean屬性:IsCanceled,IsFaulted和IsCompleted。注意,當(dāng)Task處于RanToCompleted,Canceled或者Faulted狀態(tài)時(shí),IsCompleted返回true。為了判斷一個(gè)Task是否成功完成,最簡(jiǎn)單的辦法就是使用如下所示的代碼: if (task.Status == TaskStatus.RanToCompleted )....... 如果Task是通過(guò)調(diào)用以下某個(gè)函數(shù)來(lái)創(chuàng)建的,這個(gè)Task對(duì)象就處于WaitingForActivation狀態(tài):ContinueWith、ContinueWithAll,ContinueWithAnv或者FromAsnc。如果通過(guò)構(gòu)造一個(gè)TaskCompletionSource<TResult>對(duì)象[表示未綁定到委托的?Task<TResult>?的制造者方,并通過(guò)?Task?屬性提供對(duì)使用者方的訪問(wèn)]創(chuàng)建一個(gè)Task,該Task在創(chuàng)建時(shí)也處于WaitingForActivation狀態(tài)。這個(gè)狀態(tài)意味著該Task的調(diào)度由任務(wù)基礎(chǔ)結(jié)構(gòu)控制。例如,不能顯式啟動(dòng)一個(gè)通過(guò)ContinueWith創(chuàng)建的對(duì)象。這個(gè)Task會(huì)在它的先驅(qū)任務(wù)(antecedent task)執(zhí)行完畢后自動(dòng)開始。 5.6任務(wù)工廠 ? 有的時(shí)候,可能需要?jiǎng)?chuàng)建一組Task任務(wù)來(lái)共享相同的狀態(tài)。為了避免機(jī)械地將相同的參數(shù)傳給每一個(gè)Task的構(gòu)造器,可以創(chuàng)建一個(gè)任務(wù)工廠來(lái)封裝通用的狀態(tài),System.Threding.Tasks命名空間定義了一個(gè)TaskFactory類型和一個(gè)TaskFactory<TResult>類型。兩個(gè)類型都派生自System.Object;也就是說(shuō),它們是平級(jí)的。 如果要?jiǎng)?chuàng)建的是一組沒(méi)有返回值的任務(wù),那么要構(gòu)造一個(gè)TaskFactory;如果要?jiǎng)?chuàng)建的是一組有一個(gè)特定返回值的任務(wù),那么要構(gòu)造一個(gè)TaskFactory<TResult>,并通過(guò)泛型TResult實(shí)參來(lái)傳遞任務(wù)的返回類型。創(chuàng)建任何任務(wù)工廠類時(shí),要向它的構(gòu)造器傳遞一些默認(rèn)值。工廠創(chuàng)建的任務(wù)都將具有這些默認(rèn)值。具體的說(shuō),要想工廠傳遞你希望工廠創(chuàng)建的任務(wù)具有的CancellationToken,TaskScheduler,TaskCreationOptions和TaskContinuationOptions設(shè)置。 以下實(shí)例代碼演示了如何使用一個(gè)TaskFatory: 1 private static void Test5() 2 { 3 var parent = new Task(() => 4 { 5 var cts = new CancellationTokenSource(); 6 var tf = new TaskFactory<Int32>(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 7 8 // 這個(gè)任務(wù)創(chuàng)建并啟動(dòng)三個(gè)子任務(wù) 9 var childTasks = new[] { 10 tf.StartNew(() => Sum(cts.Token, 10000)), 11 tf.StartNew(() => Sum(cts.Token, 20000)), 12 tf.StartNew(() => Sum(cts.Token, Int32.MaxValue)) , // 太大,拋出 OverflowException異常 13 tf.StartNew(() => Sum(cts.Token, 30000)) 14 }; 15 16 // 如果子任務(wù)拋出異常就取消其余子任務(wù) 17 for (Int32 task = 0; task < childTasks.Length; task++) 18 childTasks[task].ContinueWith(t => cts.Cancel(), TaskContinuationOptions.OnlyOnFaulted); 19 20 // 所有子任務(wù)完成后,從未出錯(cuò)/未取消的任務(wù)返回的值, 21 // 然后將最大值傳給另一個(gè)任務(wù)來(lái)顯示結(jié)果 22 tf.ContinueWhenAll(childTasks, 23 completedTasks => completedTasks.Where(t => !t.IsFaulted && !t.IsCanceled).Max(t => t.Result), 24 CancellationToken.None) 25 .ContinueWith(t => Console.WriteLine("The maximum is: " + t.Result), 26 TaskContinuationOptions.ExecuteSynchronously); 27 }); 28 // 子任務(wù)完成后,也顯示任何未處理的異常 29 parent.ContinueWith(p => 30 { 31 // 將所有文本放到一個(gè) StringBuilder 中并只調(diào)用 Console.WrteLine 一次 32 // 因?yàn)檫@個(gè)任務(wù)可能和上面任務(wù)并行執(zhí)行,而我不希望任務(wù)的輸出變得不連續(xù) 33 StringBuilder sb = new StringBuilder("The following exception(s) occurred:" + Environment.NewLine); 34 foreach (var e in p.Exception.Flatten().InnerExceptions) 35 sb.AppendLine(" " + e.GetType().ToString()); 36 Console.WriteLine(sb.ToString()); 37 }, TaskContinuationOptions.OnlyOnFaulted); 38 39 // 啟動(dòng)父任務(wù),便于它啟動(dòng)子任務(wù) 40 parent.Start(); 41 } TaskFactoryTest 通過(guò)上述代碼,創(chuàng)建了一個(gè)TaskFactory<Int32>對(duì)象。這個(gè)任務(wù)工廠將用于創(chuàng)建3個(gè)Task對(duì)象。希望它做4件事:每個(gè)Task對(duì)象都共享相同的CancellationTokenSource.Toke,其中3個(gè)任務(wù)被視為其父任務(wù)的子任務(wù),TaskFactory對(duì)象創(chuàng)建的所有延續(xù)任務(wù)都同步執(zhí)行,而且這個(gè)TaskFactory創(chuàng)建的所有Task對(duì)象都是用默認(rèn)的TaskScheduler。 然后創(chuàng)建一個(gè)數(shù)組,其中包含了3個(gè)子Task對(duì)象,所有都是通過(guò)TaskFactory的StartNew方法來(lái)創(chuàng)建的。使用這個(gè)方法,可以方便的創(chuàng)建并啟動(dòng)每個(gè)子任務(wù)。在一個(gè)循環(huán)中,告訴每個(gè)子任務(wù),如果拋出一個(gè)未處理的異常,就會(huì)取消其它仍在運(yùn)行的所有子任務(wù)。最后,在TaskFacroty上調(diào)用ContinueWithAll,它創(chuàng)建一個(gè)在所有子任務(wù)都結(jié)束后運(yùn)行的一個(gè)Task。由于這個(gè)任務(wù)是用TaskFactory創(chuàng)建的,所以它仍然被視為父任務(wù)的一個(gè)子任務(wù),會(huì)使用默認(rèn)的TaskScheduler同步執(zhí)行。然而,希望即使其他子任務(wù)被取消,也要運(yùn)行這個(gè)任務(wù)。因此,我傳遞CancellationToken.None來(lái)覆蓋TaskFactory的CancellationToken。這會(huì)造成該任務(wù)完全不能取消。最后,當(dāng)處理所有結(jié)果的任務(wù)完成后,創(chuàng)建另一個(gè)任務(wù)來(lái)顯示從所喲子任務(wù)中返回的最大值。 注意:調(diào)用TaskFactory或TaskFactory<TRsult>的靜態(tài)ContinueWhenAll和ContinueWhenAny方法時(shí),以下TaskContinuationOption標(biāo)志是非法的:NotOnRanToComplettion,NoyOnFaulted和NotCanceled。也就是說(shuō),無(wú)論先驅(qū)任務(wù)是如何完成的,ContinueWhenAll和ContinueWhenAny方法時(shí)都會(huì)執(zhí)行延續(xù)任務(wù)。 5.7 ?任務(wù)調(diào)度器(TaskScheduler)? 任務(wù)基礎(chǔ)結(jié)構(gòu)是很靈活的,其中TaskScheduler對(duì)象功不可沒(méi)。TaskScheduler對(duì)象負(fù)責(zé)執(zhí)行調(diào)度的任務(wù),同時(shí)向Visual Studio 調(diào)試器公開任務(wù)信息。FCL提供了兩個(gè)派生自TaskScheduler的類型:線程池任務(wù)調(diào)度器(thread pool task scheduler)和同步上下文任務(wù)調(diào)度器(synchronization context task scheduler)。 默認(rèn)情況下,所有應(yīng)用程序使用的都是線程池任務(wù)調(diào)度器。這個(gè)任務(wù)調(diào)度器將任務(wù)調(diào)度給線程池的工作者線程,將在后面進(jìn)行更詳細(xì)的討論。可以查詢TaskScheduler的靜態(tài)Default屬性來(lái)獲得對(duì)默認(rèn)任務(wù)調(diào)度器的一個(gè)引用。. ?同步上下文任務(wù)調(diào)度器通常用于Windows窗體、WPF和Silverlight應(yīng)用程序。這個(gè)任務(wù)調(diào)度器將所有任務(wù)都調(diào)度給應(yīng)用程序的GUI線程,是所有任務(wù)代碼都能成功更新UI,比如按鈕。菜單項(xiàng)等。同步上下文任務(wù)調(diào)度器根本不使用線程池。可以查詢TaskScheduler的FromCurrentSynchronizationContext方法來(lái)獲取對(duì)一個(gè)同步上下文任務(wù)調(diào)度器的引用。

~~~~~~待續(xù)。。。。

?

復(fù)制去Google翻譯翻譯結(jié)果

轉(zhuǎn)載于:https://www.cnblogs.com/dacude/p/4391393.html

總結(jié)

以上是生活随笔為你收集整理的26计算限制的异步操作01-CLR的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。