標籤:number opera line read repr sql 通知 調度 依賴
簡介
.NET 4包含新名稱空間System.Threading.Tasks,它 包含的類抽象出了線程功能。 在後台使用ThreadPool。 任務表示應完成的某個單元的工作。 這個單元的工作可以在單獨的線程中運行,也可以以同步方式啟動一個任務,這需要等待主調線程。 使用任務不僅可以獲得一個抽象層,還可以對底層線程進行很多控制。
在安排需要完成的工作時,任務提供了非常大的靈活性。 例如,可 以定義連續的工 作—— 在一個任務完成後該執行什麼工作。 這可以區分任務成功與否。 另外,還可以在階層中安排任務。例如,父任務可以建立新的子任務。 這可以建立一種依賴關係,這樣,取消父任務,也會取消其子任務。
啟動任務
要啟動任務,可 以使用 TaskFactory類 或 Task類 的建構函式和 Start()方法。Task類的建構函式在建立任務上提供的靈活性較大。
在啟動任務時,會建立Task類 的一個執行個體,利用Action或Action<object>委託不帶參數或帶一個object參數 ,可以指定應啟動並執行代碼,這類似於Thread類 。下面定義了一個無參數的方法。 在實現代碼中,把任務的ID寫入控制台中:
static void TaskMethod(){ Console.WriteLine("running in a task"); Console.WriteLine("Task id: {0}",Task.CurrentId);}
在上面的代碼中,可 以看到啟動新任務的不同方式。第一種方式 使用執行個體化TaskFactory類 ,在其中把 TaskMedlod()方 法傳遞給StartNew()方法,就會立即啟動任務。 第二種方式使用 Task類的建構函式。 執行個體化 Task對象時,任務不會立即運行,而是指定 Created狀態。接著調用 Task類的Start()方法,來啟動任務。 使用Task類 時,除了調用 Start()方法,還可以調用RunSynchronously()方法。這樣,任務也會啟動,但在調用者的當前線程中它正在運行,調用者需要一直等待到該任務結束。 預設情況下,任務是非同步啟動並執行。
//using task factory TaskFactory tf = new TaskFactory(); Task t1 = tf.StartNew(TaskMethod); //using the task factory via a task Task t2 = Task.TaskFactory.StartNew(TaskMethod); //using Task constructor Task t3 = new Task(TaskMethod); t3.Start();
使用Task類的建構函式和TaskFactory類的StartNew()方法時,都可以傳遞TaskCreationOptions枚舉中的值。設定LongRunning選項,可以通知任務調度器,該任務需要較長時間執行,這樣調度器更可能使用新線程。如果該任務應關聯到父任務上,而父任務取消了,則該任務也應取消,此時應設定 AuachToParent選項。PreferFairness的值表示,調度器應提取出已在等待的第一個任務。 如果一個任務在另一個任務內部建立,這就不是預設情況 。如果任務使用子任務建立了其他工作,子任務就優先於其他任務。 它們不會排線上程池隊列中的最後。 如果這些任務應以公平的方式與所有其他任務一起處理,就設定該選項為PreferFairness。
Task t4 = new Task(TaskMethod, TaskCreationOptions.PreferFairness);t4.Start();
連續任務
通過任務,可 以指定在任務完成後,應開始運行另一個特定任務,例如,一個使用前一個任務的結果的新任務,如 果前一個任務失敗了,這個任務就應執行一些清理工作。
任務處理常式或者不帶參數或者帶一個對象參數,而連續處理常式有一個 Task類 型的參數,這裡可以訪問起始任務的相關資訊:
static void DoOnFirst(){ Console.WriteLine("doing some task {0}",Task.CurrentId); Thread.Sleep(3000);}static void DoOnSecond(Task t){ Console.WriteLine("task {0} finished", t.Id); Console.WriteLine("this task id {0}", Task.CurrentId); Console.WriteLine("do some cleanup"); Thread.Sleep(3000);}
連續任務通過在任務上調用ContinueWith()方法來定義。也可以使用TaskFactory類來定義。t1.ContinueWith(DoOnSecond)方 法表示,調用DoOnSecond()方法的新任務應在任務t1結束時立即啟動。在一個任務結束時,可以啟動多個任務,連續任務也可以有另一個連續任務,如下面的例子所示:
Task t1 = new Task(DoOnFirst);Task t2 = t1.ContinueWith(DoOnSecond);Task t3 = t1.ContinueWith(DoOnSecond);Task t4 = t2.ContinueWith(DoOnSecond);
無論前一個任務是如何結束的,前 面的連續任務總是在前一個任務結束時啟動。 使用TaskContinuationOptions枚舉中的值,可以指定,連續任務只有在起始任務(或失敗)結束時啟動。一些可能的值是OnlyOnFaulted、 NotOnFaulted、 OnlyOnCanceled、 NotOnCanceled和OnlyOnRanToCompletion。
Task t5 = t1.ContinueWith(DoOnError, TaskContinuationOptions.OnlyOnFaulted);
任務階層
利用任務連續性,可 以在一個任務結束後啟動另一個任務。 任務也可以構成一個階層。 一個任務啟動一個新任務時,就啟動了一個父/子階層。
下面的程式碼片段在父任務內部建立一個任務。 建立子任務的代碼與建立父任務的代碼相同,唯一的區別是這個任務從另一個任務內部建立。
static void ParentAndChild(){ var parent = new Task(ParentTask); parent.Start(); Thread.Sleep(2000); Console.WriteLine(parent.Status); Thread.Sleep(4000); Console.WriteLine(parent.Status);}static void ParentTask(){ Console.WriteLine("task id {0}", Task.CurrentId); var child = new Task(ChildTask); child.Start(); Thread.Sleep(1000); Console.WriteLine("parent started child");}static void ChildTask(){ Console.WriteLine("child"); Thread.Sleep(5000); Console.WriteLine("child finished");}
如果父任務在子任務之前結束,父任務的狀態就顯示為WaitingForChildrenToComplete。 只要子任務也結束時,父任務的狀態就變成RanToCompletion。 當然,如 果父任務用TaskCreationOptions枚舉中的 DetachedFromParent建立子任務時,這就無效。
任務的結果
任務結束時,它可以把一些有用的狀態資訊寫到共用對象中。這個共用對象必須是安全執行緒的。另一個選項是使用返回某個結果的任務。使用 Task類 的泛型版本,就可以定義返回某個結果的任務的傳回型別。
為了返回某個結果任務調用的方法可以聲明為帶任意傳回型別。樣本方法TaskWithResult()利用一個元組返回兩個int值。 該方法的輸入可以是void或object類型,如下所示:
static Tuple<int, int> TaskWithResult(object division){ Tuple<int, int> div =(Tuple<int, int>)division; int result = div.Item1/div.Item2; int reminder = div.Item1%div.Item2; Console.WriteLine("task creates a result..."); return Tuple.Create<int, int>(result, reminder);}
定義一個調用 StartWithResult()方法的任務時,要使用泛型類Task<Tresult>。 泛型參數定義了傳回型別。通過建構函式,把這個方法傳遞給Func委 托,第二個參數定義了輸入值。 因為這個任務在object參數中需要兩個輸入值,所以還建立了一個元組。 接著啟動該任務。 Task執行個體t1的Result屬性被禁用,並一直等到該任務完成。任務完成後,Result屬性包含任務的結果。
var t1 = new Task<Tuple<int, int>>(TaskWithResult, Tuple.Create<int, int>(8, 3));t1.Start();Console.WriteLine(t1.Result);t1.Wait();Console.WriteLine("result from task: {0} {1}",t1.Result.Item1, t1.Result.Item2);
備忘:上例中,Task<Tresult>建構函式調用了TaskFactory.StartNew 方法的 (Func<Object, TResult>, Object)重載。
function
類型:System.Func<Object, TResult>
一個函數委託,可返回能夠通過任務獲得的將來結果。
state
類型:System.Object
包含 function 委託所用資料的對象。
因此我們可以知道,為什麼在執行個體化t1的時候,為什麼要建立一個新的Tuple對象了。
[深入學習C#]C#實現多線程的方式:Task——任務