程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> .Net4.0 任務(Task),.net4.0任務task

.Net4.0 任務(Task),.net4.0任務task

編輯:C#入門知識

.Net4.0 任務(Task),.net4.0任務task


任務(Task)是一個管理並行工作單元的輕量級對象。它通過使用CLR的線程池來避免啟動專用線程,可以更有效率的利用線程池。System.Threading.Tasks 命名空間下任務相關類一覽:

類 作用 Task 管理工作單元 Task<TResult> 管理帶返回值的工作單元 TaskFactory 創建任務 TaskFactory<TResult> 創建任務或者有相同返回值的延續任務 TaskScheduler 管理任務調度 TaskCompletionSource 手動控制任務工作流

任務用來並行地執行工作,充分地利用多核:事實上,Parallel和PLINQ內部就是建立在任務並行的結構上。

任務提供了一系列強大的特性來管理工作單元,包括:

  • 協調任務調度
  • 建立一個任務從另一個任務中啟動的父子關系
  • 實現合作取消(cooperative cancellation)模式
  • 無信號的任務等待
  • 附加延續任務(continuation)
  • 基於多個祖先任務調度一個延續任務
  • 傳遞異常到父任務、延續任務或任務消費者

同時任務實現了一個本地工作隊列,它允許你高效地創建快速執行的子任務而不用遭受在單個工作隊列時的競爭花費。任務並行庫讓你用最小的花費來創建成百上千的任務,但是如果你想創建上百萬個任務,就必須分割這些任務到更大的工作單元,以保持效率。

創建與啟動任務

有兩種方法可以創建任務,一種是通過TaskFactory的StartNew()方法創建並啟動任務;另一種是調用Task構造函數創建,然後手動啟動任務。需要注意的是,任務啟動後並不會立即執行,它是由任務調度器(TaskScheduler)來管理的。

  • TaskFactory的StartNew()方法創建任務的示例如下:
    //沒有返回值
    Task.Factory.StartNew(() => Console.WriteLine("Task Created!"));
    //有返回值
    var task = Task.Factory.StartNew<string>(() => "Task Created!");
    Console.WriteLine(task.Result);
  • 調用Start方法手動啟動的示例如下:
    var task = new Task<string>(() => "Task Created!");
    task.Start();//異步執行
    Console.WriteLine(task.Result);
  • 調用RunSynchronously方法手動啟動的示例如下:
    var task = new Task<string>(() => "Task Created!");
    task.RunSynchronously();//同步執行
    Console.WriteLine(task.Result);

也可以在創建任務時指定一個任務狀態參數,可以通過任務的AsyncState屬性來訪問該參數。示例:

var task = Task.Factory.StartNew(state => "hello " + state, "Mike");
Console.WriteLine(task.AsyncState);
Console.WriteLine(task.Result);

你還可以指定一個任務創建選項(TaskCreationOptions) ,這個枚舉類型有以下枚舉值:None,LongRunning,PreferFairness,AttachedToParent。下面解釋各個枚舉值的作用。

  • LongRunning:顧名思義就是長時間運行的任務,此選項建議任務調度器分配一個專用的線程給任務。這樣做的原因是:長時間運行的任務可能會阻塞任務隊列,導致那些短小的任務一直得不到執行。LongRunning也適合那些阻塞的任務。
  • PreferFairness:公平第一,此選項建議任務調度器盡量按照任務的啟動時間來調度任務。但是它通常可能不這樣做,因為它使用本地工作偷取隊列(local work-stealing queues)優化任務調度。這個優化對那些非常小的任務很有用。
  • AttachToParent:附加到父任務,此選項用來創建子任務。創建子任務示例: 復制代碼
    第一種方式:
    var parent = Task.Factory.StartNew(() =>
    {
        var nonChildTask = Task.Factory.StartNew(
            () => Console.WriteLine("I'm not a child task.")
        );
        var childTask = Task.Factory.StartNew(
            () => Console.WriteLine("I'm a child task."), 
        TaskCreationOptions.AttachedToParent);
    }); 
    第二種方式:
    Task parent=new Task(()=>
    {
        DoStep1();
    });
    Task task2 = parent.ContinueWith ((PrevTask) =>
    {
        DoStep2();
    });
    parent.Start();
    復制代碼

任務等待

任務可以通過Wait()成員方法或Result屬性來等待任務完成。

當調用Result屬性時,將會執行下列操作:

Task.WaitAny()靜態方法等待任何一個任務完成。示例:

復制代碼
var tasks = new Task[3];
for (int i = 0; i < tasks.Length; i++)
{
    int taskIndex = i;
    tasks[i] = Task.Factory.StartNew(() =>
    {
        int seed=Guid.NewGuid().GetHashCode();
        int waitTime = new Random(seed).Next(10, 100);
        Thread.Sleep(waitTime);
        Console.WriteLine("Task{0} Finished", taskIndex);
    });
}
Task.WaitAny(tasks);
Console.WriteLine("已有任務完成");
復制代碼

Task.WaitAll()靜態方法等待所有任務完成。即使有任務拋出異常也不會終止等待,它會在所有任務完成之後拋出一個AggregateException異常,這個異常聚合了所有任務拋出的異常。示例:

復制代碼
var tasks = new Task[3];
for (int i = 0; i < tasks.Length; i++)
{
    int taskIndex = i;
    tasks[i] = Task.Factory.StartNew(() =>
    {
         int waitTime = new Random(Guid.NewGuid().GetHashCode()).Next(10, 100);
         Thread.Sleep(waitTime);
         Console.WriteLine("Task{0} Finished", taskIndex);
    });
}
Task.WaitAll(tasks);
Console.WriteLine("所有任務完成");
復制代碼

異常處理

默認情況下任務未處理的異常會終止應用程序。需要指出的是任務中未處理的異常不會立即導致應用程序終止,異常要延遲到垃圾回收器回收任務並調用Finalize方法時才會終止程序。如果讀取了任務的Exception屬性,這個操作將阻止隨後的應用程序終止。  當等待任務完成時,所有未處理的異常會傳遞到調用方。  Wait()方法超時的異常也必須處理,否則導致應用程序終止。  子任務中未處理的異常會冒泡傳遞到父任務;嵌套任務中的非子任務的異常不會傳遞到這個任務的上一層任務,需要單獨處理,否則將導致應用程序終止。

復制代碼
var task = Task.Factory.StartNew(() =>
{
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
    Task.Factory.StartNew(() => { throw null; }, TaskCreationOptions.AttachedToParent);
});
task.Wait();
復制代碼

TaskScheduler.UnobservedTaskException靜態事件提供了最後一種手段處理所有未處理異常。通過處理這個事件,就不用終止應用程序,而用你自己的異常處理邏輯替代它。

取消任務

當創建任務時,可以傳入一個取消令牌(CancelationToken)參數,這樣就可以安全的取消任務。示例:

復制代碼
var source = new CancellationTokenSource();
var token = source.Token;
var task = Task.Factory.StartNew(() =>
{
    Console.WriteLine("Task starting...");
    while (true)
    {
        token.ThrowIfCancellationRequested();
        Console.WriteLine("I'm alive. {0}",DateTime.Now);
        Thread.Sleep(1000);
    }
},token);

Task.Factory.StartNew(() =>
{
    Thread.Sleep(4500);
    source.Cancel();
});

try
{
    task.Wait();
    Console.WriteLine("Task stopped.");
}
catch (AggregateException e)
{
    if (e.InnerException is OperationCanceledException)
    {
        Console.WriteLine("Task canceled.");
    }
    else
    {
        Console.WriteLine("errors.");
    }
}
復制代碼

通過調用CancellationTokenSource的Cancel()方法取消任務,這個並不會立即終止任務,一直延遲到任務下次檢測是否取消時才通過拋出OperationCanceledException終止任務。

如果想通過直接拋出OperationCanceledException異常的方式取消任務,則需要在任務中傳入CancelationToken參數,否則就不能將任務的狀態為TaskStatus.Canceled並觸發OnlyOnCanceled延續任務。

此外,取消令牌也可以傳遞到Wait和CancelAndWait方法中來取消等待。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved