[C#][.NET]Task

Task

Task

延續上一篇分享的執行緒池,執行緒池的使用還是有些許缺點,例如並不知道操作什麼時候會結束,無法有回傳值。所以 .NET Framework 提供了一個 Task的概念,Task可以知道工作什麼時候結束,而且結束的時候可以回傳一個Result。

 

Task用法

當Task任務結束後,可以從Result屬性取得該結果職,此時如果Task還沒結束,則執行緒會停在取Result屬性的地方,直到任務完成。

public class TaskDemo
{
    public void Run() 
    {
        Task<int> task = Task.Run<int>
                        (
                            () => Enumerable.Range(0,10000).Sum(x => x)
                        );

        Console.WriteLine("程式執行中....");
        Console.WriteLine("取得結果:{0}",task.Result); // 如果Task還沒結束,則執行緒會停在這邊
    }
}

 

接續工作(Continuation)

任務結束後,可直自動呼叫接續作業,不必等到取Result值才做後續動作。

Task<int> task = Task.Run<int>
    (
        () => Enumerable.Range(0, 10000).Sum(x => x)
    );
task.ContinueWith(x => 
{
    // Task任務結束後,會直接跑這段程式碼
    var sum = x.Result;
    Console.WriteLine("接續作業取值:{0}",sum);
});

Console.WriteLine("程式執行中....");            
Console.WriteLine("取得結果:{0}", task.Result); // 使用接續作業,不影響其他地方取得Result屬性

用ContinueWith的方式,當Task出錯,則Exception會在整合在AggregateException之中。

 

另一種接續工作方式(GetAwaiter)

Task<int> task = Task.Run<int>
    (
        () => Enumerable.Range(0, 10000).Sum(x => x)
    );
TaskAwaiter<int> awaiter = task.GetAwaiter();
awaiter.OnCompleted(() => 
{
    // Task任務結束後,會直接跑這段程式碼
    var sum = awaiter.GetResult();
    Console.WriteLine("接續作業取值:{0}", sum);
});

Console.WriteLine("程式執行中....");
Console.WriteLine("取得結果:{0}", task.Result); // 使用接續作業,不影響其他地方取得Result屬性

C# 5.0的非同步功能就是使用這種接續方式。  此方法會在Task完成或出錯之後執行一個委派,如果Task出現錯誤,那麼接續工作呼叫awaiter.GetResult()就是拋出例外,且此例外是直接拋出,並不會整合在AggregateException之中。

 

CancellationToken

如果執行緒執行時間太長,.NET Framework有提供特殊的類別,可以取消該Task的工作,稱CancellationToken。  使用CancellationToke要特別注意取消時會拋出OperationCanceledException,並且該Exception會被整合在AggregateException之中。

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken token = cancellationTokenSource.Token;

Task task = Task.Run(() =>
{
    while (!token.IsCancellationRequested)
    {
        Console.WriteLine("執行中....");
        Thread.Sleep(3000);
    }

    // 接受到取消的指令,拋出OperationCanceledException,停止Task的執行
    token.ThrowIfCancellationRequested();
}, token);

try
{
    Console.WriteLine("Press enter to cancel the task");
    Console.ReadLine();

    // 取消Task執行
    cancellationTokenSource.Cancel();
    
    // 等待任務完成
    task.Wait();

}
catch (AggregateException e)
{
    Console.WriteLine(e.InnerExceptions[0].Message);
    Console.WriteLine("已取消完成");
}

 

Task是ThreadPool的進化版,幫我們封裝了很多方便的功能,又因為是從ThreadPool取得執行緒,效率上也很快。

 

 

一天一分享,身體好健康。

該追究的不是過去的原因,而是現在的目的。