在C#4.0之前需要執(zhí)行一個(gè)復(fù)雜的異步操作時(shí),只能使用CLR線程池技術(shù)來執(zhí)行一個(gè)任務(wù),。線程池執(zhí)行異步任務(wù)時(shí),不知道任務(wù)何時(shí)完成,以及任務(wù)的在任務(wù)完成后不能獲取到返回值,。但是在C#4.0中引人了一個(gè)的任務(wù)(System.Threading.Tasks命名空間的類型)機(jī)制來解決異步操作完成時(shí)間和完成后返回值的問題。 1.使用Task類創(chuàng)建并執(zhí)行簡單任務(wù) 通過使用Task的構(gòu)造函數(shù)來創(chuàng)建任務(wù),,并調(diào)用Start方法來啟動(dòng)任務(wù)并執(zhí)行異步操作,。創(chuàng)建任務(wù)時(shí),必須傳遞一個(gè)Action或Action<Object>類型的委托回調(diào)方法,,可以選擇的傳遞任務(wù)執(zhí)行時(shí)說需要的數(shù)據(jù)對象等,。Task類的構(gòu)造函數(shù)如下: public Task(Action action); public Task(Action<object> action, object state); public Task(Action action, CancellationToken cancellationToken); public Task(Action action, TaskCreationOptions creationOptions); public Task(Action<object> action, object state, CancellationToken cancellationToken); public Task(Action<object> action, object state, TaskCreationOptions creationOptions); public Task(Action action, CancellationToken cancellationToken, TaskCreationOptions creationOptions); public Task(Action<object> action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions); 示例代碼: static void Main(string[] args) { Console.WriteLine("主線程執(zhí)行業(yè)務(wù)處理."); //創(chuàng)建任務(wù) Task task = new Task(() =>{ Console.WriteLine("使用System.Threading.Tasks.Task執(zhí)行異步操作."); for (int i = 0; i < 10; i++) { Console.WriteLine(i); } }); //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主線程執(zhí)行其他處理"); //主線程掛起1000毫秒,等待任務(wù)的完成,。 Thread.Sleep(1000); } 任務(wù)調(diào)度結(jié)果: 2.等待任務(wù)的完成并獲取返回值 使用任務(wù)執(zhí)行異步操作時(shí),,最主要的是要后的任務(wù)完成時(shí)的返回值。在任務(wù)類中有一個(gè)實(shí)例方法Wait(有許多重載版本)他能等待任務(wù)的完成,,我們也可以通過Task類的派生類Task<TResult>創(chuàng)建一個(gè)異步任務(wù),,并指定任務(wù)完成時(shí)返回值的類型,這樣可以通過Task<TResult>的實(shí)例對象獲取到任務(wù)完成后的返回值,。創(chuàng)建一個(gè)異步任務(wù)并執(zhí)行0到100求和操作返回最后的計(jì)算結(jié)果,,示例代碼: static void TaskWait() { //創(chuàng)建任務(wù) Task<int> task = new Task<int>(() => { int sum = 0; Console.WriteLine("使用Task執(zhí)行異步操作."); for (int i = 0; i < 100; i++) { sum += i; } return sum; }); //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主線程執(zhí)行其他處理"); //等待任務(wù)的完成執(zhí)行過程。 task.Wait(); //獲得任務(wù)的執(zhí)行結(jié)果 Console.WriteLine("任務(wù)執(zhí)行結(jié)果:{0}", task.Result.ToString()); } 執(zhí)行結(jié)果: Task類還有一些靜態(tài)方法,,WaitAll用于等待提供的所有 System.Threading.Tasks.Task 對象完成執(zhí)行過程和Wait用于等待提供的任一個(gè) System.Threading.Tasks.Task 對象完成執(zhí)行過程,,這兩個(gè)方法都有一些重載版本。 //等待所有任務(wù)完成 public static void WaitAll(params Task[] tasks); //等待任意一個(gè)任務(wù)完成 public static int WaitAny(params Task[] tasks); 3.使用ContinueWith方法在任務(wù)完成時(shí)啟動(dòng)一個(gè)新任務(wù) 在使用能夠Task類的Wait方法等待一個(gè)任務(wù)時(shí)或派生類的Result屬性獲得任務(wù)執(zhí)行結(jié)果都有可能阻塞線程,,為了解決這個(gè)問題可以使用ContinueWith方法,,他能在一個(gè)任務(wù)完成時(shí)自動(dòng)啟動(dòng)一個(gè)新的任務(wù)來處理執(zhí)行結(jié)果。 示例代碼: static void TaskContinueWith() { //創(chuàng)建一個(gè)任務(wù) Task<int> task = new Task<int>(() => { int sum = 0; Console.WriteLine("使用Task執(zhí)行異步操作."); for (int i = 0; i< 100; i++) { sum += i; } return sum; }); //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) task.Start(); Console.WriteLine("主線程執(zhí)行其他處理"); //任務(wù)完成時(shí)執(zhí)行處理,。 Task cwt = task.ContinueWith(t => { Console.WriteLine("任務(wù)完成后的執(zhí)行結(jié)果:{0}", t.Result.ToString()); }); Thread.Sleep(1000); } 執(zhí)行結(jié)果: 上述示例中任務(wù)不是等待完成來顯示執(zhí)行結(jié)果,,而是使用ContinueWith方法,它能夠知道任務(wù)在什么時(shí)候完成并啟動(dòng)一個(gè)新的任務(wù)來執(zhí)行任務(wù)完成后的處理。ContinueWith方法具有一些重載版本,,這些重載版本允許指定延續(xù)任務(wù)需要使用的數(shù)據(jù),、延續(xù)任務(wù)的工作方式(System.Threading.Tasks.TaskContinuationOptions的枚舉值按位OR運(yùn)行的結(jié)果)等。 4.創(chuàng)建父子任務(wù)和任務(wù)工廠的使用 通過Task類創(chuàng)建的任務(wù)是頂級任務(wù),,可以通過使用 TaskCreationOptions.AttachedToParent 標(biāo)識(shí)把這些任務(wù)與創(chuàng)建他的任務(wù)相關(guān)聯(lián),,所有子任務(wù)全部完成以后父任務(wù)才會(huì)結(jié)束操作。示例如下: static void ParentChildTask() { Task<string[]> parent = new Task<string[]>(state => { Console.WriteLine(state); string[] result = new string[2]; //創(chuàng)建并啟動(dòng)子任務(wù) new Task(() => { result[0] = "我是子任務(wù)1,。"; }, TaskCreationOptions.AttachedToParent).Start(); new Task(() => { result[1] = "我是子任務(wù)2,。"; }, TaskCreationOptions.AttachedToParent).Start(); return result; },"我是父任務(wù),并在我的處理過程中創(chuàng)建多個(gè)子任務(wù),,所有子任務(wù)完成以后我才會(huì)結(jié)束執(zhí)行,。"); //任務(wù)處理完成后執(zhí)行的操作 parent.ContinueWith(t => { Array.ForEach(t.Result, r=>Console.WriteLine(r)); }); //啟動(dòng)父任務(wù) parent.Start(); Console.Read(); } 執(zhí)行結(jié)果: 如果需要?jiǎng)?chuàng)建一組具有相同狀態(tài)的任務(wù)時(shí),可以使用TaskFactory類或TaskFactory<TResult>類,。這兩個(gè)類創(chuàng)建一組任務(wù)時(shí)可以指定任務(wù)的CancellationToken,、TaskCreationOptions、TaskContinuationOptions和TaskScheduler默認(rèn)值,。 示例代碼: static void TaskFactoryApply(){ Task parent = new Task(() => { CancellationTokenSource cts = new CancellationTokenSource(5000); //創(chuàng)建任務(wù)工廠 TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); //添加一組具有相同狀態(tài)的子任務(wù) Task[] task = new Task[]{ tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第一個(gè)任務(wù),。"); }), tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第二個(gè)任務(wù)。"); }), tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第三個(gè)任務(wù),。"); }) }; }); parent.Start(); Console.Read(); } } 執(zhí)行結(jié)果: 5.任務(wù)內(nèi)部實(shí)現(xiàn)和任務(wù)調(diào)度 任務(wù)內(nèi)部有一組構(gòu)成任務(wù)狀態(tài)的屬性,,標(biāo)識(shí)任務(wù)的唯一Id、表示任務(wù)的執(zhí)行狀態(tài)(TaskStatus),、任務(wù)創(chuàng)建時(shí)提供的回調(diào)函數(shù)的引用和傳遞給回調(diào)函數(shù)的數(shù)據(jù)對象AsyncState,、對任務(wù)創(chuàng)建時(shí)的任務(wù)調(diào)度對象(TaskScheduler)的引用、對父任務(wù)的引用以及對執(zhí)行上下文的引用和ManualResetEventSlim對象的引用,。Task類和Task<TResult>類都實(shí)現(xiàn)了標(biāo)準(zhǔn)的釋放資源的接口,,允許在任務(wù)完成處理的時(shí)候使用Dispose方法釋放資源(關(guān)閉ManualResetEventSlim對象實(shí)例)??梢允褂肨ask類的CurrentId屬性獲得正在執(zhí)行的任務(wù)的Id,,如果沒有任務(wù)在執(zhí)行CurrentId返回值為null,CurrentId是一個(gè)int,?可空類型的屬性,。任務(wù)執(zhí)行的生命周期通過TaskStatus類型的一個(gè)值來表示,TaskStatus所包含的值: public enum TaskStatus { Created = 0, WaitingForActivation = 1, WaitingToRun = 2, Running = 3, WaitingForChildrenToComplete = 4, RanToCompletion = 5, Canceled = 6, Faulted = 7, } 我們可以通過Task類的Exception屬性獲得任務(wù)在執(zhí)行過程中的所有異常,,Exception是一個(gè)AggregateException類型的屬性,。Task類提供了IsCanceled、IsCompleted,、IsFaulted屬性來獲得任務(wù)的完成狀態(tài),。通過ContinueWith,、ContinueWhenAll、ContinueWhenAny和FromAsync創(chuàng)建的后續(xù)任務(wù)都處于WaitingForActivation 狀態(tài),,這個(gè)狀態(tài)的任務(wù)會(huì)在父任務(wù)完成后自動(dòng)執(zhí)行,。 在任務(wù)內(nèi)部由TaskScheduler類調(diào)度任務(wù)的執(zhí)行,該類是一個(gè)抽象類,,F(xiàn)CL中從他派生了兩個(gè)派生類:ThreadPoolTaskScheduler線程池任務(wù)調(diào)度器和SynchronizationContextTaskScheduler同步上下文任務(wù)調(diào)度器。所有任務(wù)默認(rèn)都是采用ThreadPoolTaskScheduler調(diào)度任務(wù),,他是采用線程池來執(zhí)行任務(wù),,可以通過TaskScheduler類的靜態(tài)屬性Default獲得對默認(rèn)任務(wù)調(diào)度器的引用。SynchronizationContextTaskScheduler任務(wù)調(diào)度器能夠用在Window form,、WPF等應(yīng)用程序,,他的任務(wù)調(diào)度是采用的GUI線程,所以他能同步更新UI組件,,可以通過TaskScheduler類的靜態(tài)方法FromCurrentSynchronizationContext獲得對一個(gè)同步上下文任務(wù)調(diào)度起的引用,。 任務(wù)調(diào)度示例: private void button1_Click(object sender, EventArgs e){ //獲得同步上下文任務(wù)調(diào)度器 TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); //創(chuàng)建任務(wù),并采用默認(rèn)任務(wù)調(diào)度器(線程池任務(wù)調(diào)度器)執(zhí)行任務(wù) Task<int> task = new Task<int>(() => { //執(zhí)行復(fù)雜的計(jì)算任務(wù),。 Thread.Sleep(2000); int sum = 0; for (int i = 0; i< 100; i++) { sum += i; } return sum; }); var cts = new CancellationTokenSource(); //任務(wù)完成時(shí)啟動(dòng)一個(gè)后續(xù)任務(wù),,并采用同步上下文任務(wù)調(diào)度器調(diào)度任務(wù)更新UI組件。 task.ContinueWith(t => { this.label1.Text = "采用SynchronizationContextTaskScheduler任務(wù)調(diào)度器更新UI,。/r/n計(jì)算結(jié)果是:" + task.Result.ToString(); }, cts.Token, TaskContinuationOptions.AttachedToParent, m_syncContextTaskScheduler); task.Start(); } 執(zhí)行結(jié)果: 本文簡單的介紹了使用Task類來執(zhí)行異步操作以及任務(wù)的內(nèi)部實(shí)現(xiàn)與任務(wù)調(diào)度,。在執(zhí)行復(fù)雜異步操作時(shí),可以采用任務(wù)來執(zhí)行,,他能更好的知道異步操作在何時(shí)完成以及返回異步操作的執(zhí)行結(jié)果,。 |
|