.Net4.0 任務(Task)[轉]

來源:互聯網
上載者:User

標籤:style   blog   http   color   io   os   使用   ar   for   

.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屬性時,將會執行下列操作:

  1. 如果任務已結束,返回任務結果
  2. 如果任務已開始,等待任務結束
  3. 如果任務尚未開始執行,則在當前線程執行任務

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方法中來取消等待。

.Net4.0 任務(Task)[轉]

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.