C# 线程知识--使用Task执行异步操作(转)
http://www.codingthink.com/c/20121223/201212232246371.html
在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í)行簡(jiǎn)單任務(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ù)對(duì)象等。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);?
示例代碼:
1: static void Main(string[] args) 2: { 3: Console.WriteLine("主線程執(zhí)行業(yè)務(wù)處理."); 4: //創(chuàng)建任務(wù) 5: Task task = new Task(() => { 6: Console.WriteLine("使用System.Threading.Tasks.Task執(zhí)行異步操作."); 7: for (int i = 0; i < 10; i++) 8: { 9: Console.WriteLine(i); 10: } 11: }); 12: //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) 13: task.Start(); 14: Console.WriteLine("主線程執(zhí)行其他處理"); 15: //主線程掛起1000毫秒,等待任務(wù)的完成。 16: Thread.Sleep(1000); 17: }任務(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í)例對(duì)象獲取到任務(wù)完成后的返回值。創(chuàng)建一個(gè)異步任務(wù)并執(zhí)行0到100求和操作返回最后的計(jì)算結(jié)果,示例代碼:
1: static void TaskWait() { 2: //創(chuàng)建任務(wù) 3: Task<int> task = new Task<int>(() => 4: { 5: int sum = 0; 6: Console.WriteLine("使用Task執(zhí)行異步操作."); 7: for (int i = 0; i < 100; i++) 8: { 9: sum+=i; 10: } 11: return sum; 12: }); 13: //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) 14: task.Start(); 15: ? 16: Console.WriteLine("主線程執(zhí)行其他處理"); 17: //等待任務(wù)的完成執(zhí)行過程。 18: task.Wait(); 19: //獲得任務(wù)的執(zhí)行結(jié)果 20: Console.WriteLine("任務(wù)執(zhí)行結(jié)果:{0}", task.Result.ToString()); 21: }執(zhí)行結(jié)果:
Task類還有一些靜態(tài)方法,WaitAll用于等待提供的所有 System.Threading.Tasks.Task 對(duì)象完成執(zhí)行過程和Wait用于等待提供的任一個(gè) System.Threading.Tasks.Task 對(duì)象完成執(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é)果。
示例代碼:
1: static void TaskContinueWith() 2: { 3: //創(chuàng)建一個(gè)任務(wù) 4: Task<int> task = new Task<int>(() => 5: { 6: int sum = 0; 7: Console.WriteLine("使用Task執(zhí)行異步操作."); 8: for (int i = 0; i < 100; i++) 9: { 10: sum += i; 11: } 12: return sum; 13: }); 14: //啟動(dòng)任務(wù),并安排到當(dāng)前任務(wù)隊(duì)列線程中執(zhí)行任務(wù)(System.Threading.Tasks.TaskScheduler) 15: task.Start(); 16: Console.WriteLine("主線程執(zhí)行其他處理"); 17: //任務(wù)完成時(shí)執(zhí)行處理。 18: Task cwt = task.ContinueWith(t => { 19: Console.WriteLine("任務(wù)完成后的執(zhí)行結(jié)果:{0}", t.Result.ToString()); 20: }); 21: Thread.Sleep(1000); 22: }執(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ù)是頂級(jí)任務(wù),可以通過使用 TaskCreationOptions.AttachedToParent 標(biāo)識(shí)把這些任務(wù)與創(chuàng)建他的任務(wù)相關(guān)聯(lián),所有子任務(wù)全部完成以后父任務(wù)才會(huì)結(jié)束操作。示例如下:
1: static void ParentChildTask() { 2: Task<string[]> parent = new Task<string[]>(state => { 3: Console.WriteLine(state); 4: string[] result=new string[2]; 5: //創(chuàng)建并啟動(dòng)子任務(wù) 6: new Task(() => { result[0]= "我是子任務(wù)1。"; },TaskCreationOptions.AttachedToParent).Start(); 7: new Task(() => { result[1] = "我是子任務(wù)2。"; }, TaskCreationOptions.AttachedToParent).Start(); 8: return result; 9: },"我是父任務(wù),并在我的處理過程中創(chuàng)建多個(gè)子任務(wù),所有子任務(wù)完成以后我才會(huì)結(jié)束執(zhí)行。"); 10: //任務(wù)處理完成后執(zhí)行的操作 11: parent.ContinueWith(t => { 12: Array.ForEach(t.Result, r=>Console.WriteLine(r)); 13: }); 14: //啟動(dòng)父任務(wù) 15: parent.Start(); 16: Console.Read(); 17: }執(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)值。示例代碼:
1: static void TaskFactoryApply() 2: { 3: Task parent = new Task(() => 4: { 5: CancellationTokenSource cts = new CancellationTokenSource(5000); 6: //創(chuàng)建任務(wù)工廠 7: TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); 8: //添加一組具有相同狀態(tài)的子任務(wù) 9: Task[] task = new Task[]{ 10: tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第一個(gè)任務(wù)。"); }), 11: tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第二個(gè)任務(wù)。"); }), 12: tf.StartNew(() => { Console.WriteLine("我是任務(wù)工廠里的第三個(gè)任務(wù)。"); }) 13: }; 14: }); 15: parent.Start(); 16: Console.Read(); 17: }執(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ù)對(duì)象AsyncState、對(duì)任務(wù)創(chuàng)建時(shí)的任務(wù)調(diào)度對(duì)象(TaskScheduler)的引用、對(duì)父任務(wù)的引用以及對(duì)執(zhí)行上下文的引用和ManualResetEventSlim對(duì)象的引用。Task類和Task<TResult>類都實(shí)現(xiàn)了標(biāo)準(zhǔn)的釋放資源的接口,允許在任務(wù)完成處理的時(shí)候使用Dispose方法釋放資源(關(guān)閉ManualResetEventSlim對(duì)象實(shí)例)。可以使用Task類的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è)抽象類,FCL中從他派生了兩個(gè)派生類:ThreadPoolTaskScheduler線程池任務(wù)調(diào)度器和SynchronizationContextTaskScheduler同步上下文任務(wù)調(diào)度器。所有任務(wù)默認(rèn)都是采用ThreadPoolTaskScheduler調(diào)度任務(wù),他是采用線程池來執(zhí)行任務(wù),可以通過TaskScheduler類的靜態(tài)屬性Default獲得對(duì)默認(rèn)任務(wù)調(diào)度器的引用。SynchronizationContextTaskScheduler任務(wù)調(diào)度器能夠用在Window form、WPF等應(yīng)用程序,他的任務(wù)調(diào)度是采用的GUI線程,所以他能同步更新UI組件,可以通過TaskScheduler類的靜態(tài)方法FromCurrentSynchronizationContext獲得對(duì)一個(gè)同步上下文任務(wù)調(diào)度起的引用。
任務(wù)調(diào)度示例:
1: private void button1_Click(object sender, EventArgs e) 2: { 3: //獲得同步上下文任務(wù)調(diào)度器 4: TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext(); 5: ? 6: //創(chuàng)建任務(wù),并采用默認(rèn)任務(wù)調(diào)度器(線程池任務(wù)調(diào)度器)執(zhí)行任務(wù) 7: Task<int> task = new Task<int>(() => 8: { 9: //執(zhí)行復(fù)雜的計(jì)算任務(wù)。 10: Thread.Sleep(2000); 11: int sum = 0; 12: for (int i = 0; i < 100; i++) 13: { 14: sum += i; 15: } 16: return sum; 17: }); 18: var cts=new CancellationTokenSource(); 19: //任務(wù)完成時(shí)啟動(dòng)一個(gè)后續(xù)任務(wù),并采用同步上下文任務(wù)調(diào)度器調(diào)度任務(wù)更新UI組件。 20: task.ContinueWith(t => {this.label1.Text="采用SynchronizationContextTaskScheduler任務(wù)調(diào)度器更新UI。\r\n計(jì)算結(jié)果是:"+task.Result.ToString(); }, 21: cts.Token ,TaskContinuationOptions.AttachedToParent,m_syncContextTaskScheduler); 22: task.Start(); 23: }執(zhí)行結(jié)果:
??? 本文簡(jiǎn)單的介紹了使用Task類來執(zhí)行異步操作以及任務(wù)的內(nèi)部實(shí)現(xiàn)與任務(wù)調(diào)度。在執(zhí)行復(fù)雜異步操作時(shí),可以采用任務(wù)來執(zhí)行,他能更好的知道異步操作在何時(shí)完成以及返回異步操作的執(zhí)行結(jié)果。
轉(zhuǎn)載于:https://www.cnblogs.com/eric-xiongzw/archive/2013/01/16/2862744.html
總結(jié)
以上是生活随笔為你收集整理的C# 线程知识--使用Task执行异步操作(转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SuperSocket 1.5 Docu
- 下一篇: [转]对于非数据库字段的查询过滤以及ap