【導讀】目前私下空餘時間在研究遠程大文件斷點續傳下載,正在進行時,正當研究Task時,看到有個Unwrap方法基本沒怎麼用過,這裏記錄並學習下
說到Task.Run和Task.Factory.StartNew兩者區別以及應該推崇使用哪個,我也建議使用Task.Run,這裏不作過多介紹,網上資料一大把。web
Task代理Unwrap避免內聯promise
那麼使用該方法能夠解決什麼問題呢?首先咱們來看以下一個簡單例子微信
Task<Task<int>> call = Task.Factory.StartNew(async () =>
{
return await DoSomethingAsync();
});
咱們經過Task.Factory.StartNew異步方法,此時獲取內聯任務結果將經過嵌套Task包裹,那麼若是要是多層嵌套呢?那也沒問題,咱們能夠直接將返回結果經過var聲明便可,就不用再寫層層嵌套Task。app
這只是其一,事實狀況在於對於多層內聯Task,咱們如何處理以及取消令牌等過程當中發生的錯誤。Unwrap方法的做用就在於此,以下:異步
Task<int> call = Task.Factory.StartNew(async () =>
{
return await DoSomethingAsync();
}).Unwrap();
直接從該方法單詞入手,暫且翻譯爲「攤開或展開」,就像洋蔥般一層層剝開,讓咱們看到最終想要的東西,咱們也看看源碼怎麼講async
public static Task<TResult> Unwrap<TResult>(this Task<Task<TResult>> task)
{
if (task == null)
{
throw new ArgumentNullException(nameof(task));
}
return
!task.IsCompletedSuccessfully ? Task.CreateUnwrapPromise<TResult>(task,
lookForOce: false) :
task.Result ??
Task.FromCanceled<TResult>(new CancellationToken(true));
}
利用Unwrap方法建立一個代理,它將爲咱們處理全部複雜邏輯,同時咱們也能夠不用轉發取消令牌,它能夠爲咱們排查全部不一樣級別的錯誤,換言之,它所返回的Task包含全部令牌取消請求和異常處理(依託代理與內聯Task協調工做)就像咱們執行一個簡單的任務同樣,而不用像任務中的任務而進行嵌套編輯器
var call1 = Task.Run(async () =>
{
return await DoSomethingAsync();
});
Task.Run和Task.Factory.StartNew區別也在於此,由於利用Task.Run直接將返回最終的實際結果,因此對於內聯Task,利用Task.Run來的更加實際,而利用Task.Factory.StartNew若沒調用Unwrap可能會出現結果不符合咱們所預期學習
一樣,利用Task.Factory.StartNew進行追加任務(ContinueWith)時,也會返回一個任務內聯另一個任務,經過使用Unwrap方法便可避免這種狀況。ui
上述只是我我的對使用Unwrap方法的歸納和總結,這裏給出源碼中註釋解釋翻譯:this
![](http://static.javashuo.com/static/loading.gif)
到了這裏,想必咱們已經清楚Unwrap方法的做用,咱們來作個練習:若啓動一個長時間執行的異步任務,那麼如何中止這個異步任務?
public class UnwrapPractice
{
CancellationTokenSource cancellationToken;
public void Start()
{
cancellationToken = new CancellationTokenSource();
Task.Factory.StartNew(
function: ExecuteAsync,
cancellationToken: cancellationToken.Token,
creationOptions: TaskCreationOptions.LongRunning,
scheduler: TaskScheduler.Default);
}
public void Stop()
{
cancellationToken.Cancel();
}
async Task ExecuteAsync()
{
Console.WriteLine("進入執行操做");
while (!cancellationToken.IsCancellationRequested)
{
//模擬長時間執行操做
await Task.Delay(30000);
}
Console.WriteLine("退出執行操做");
}
}
經過上述簡單代碼實現做業練習,接下來咱們在控制檯調用啓動和中止,看看結果是否符合預期
static async Task Main(string[] args)
{
var unwrapPractice = new UnwrapPractice();
Console.WriteLine("啓動");
unwrapPractice.Start();
Thread.Sleep(3000);
Console.WriteLine("中止");
unwrapPractice.Stop();
Console.Read();
}
反觀上述動態圖,此時咱們將發現長時間異步任務經過調用取消令牌根本沒有中止(在控制檯並無打印出【退出執行操做】字眼),此時咱們可利用Unwrap方法建立此任務代理,而後在調用令牌取消方法後,再執行代理的等待方法,等待取消任務直至完成便可,貌似有點Thread中Join方法的意味,以下:
public class UnwrapPractice
{
Task task;
CancellationTokenSource cancellationToken;
public void Start()
{
cancellationToken = new CancellationTokenSource();
Task<Task> _task = Task.Factory.StartNew(
function: ExecuteAsync,
cancellationToken: cancellationToken.Token,
creationOptions: TaskCreationOptions.LongRunning,
scheduler: TaskScheduler.Default);
//獲取此任務代理
task = _task.Unwrap();
}
public void Stop()
{
cancellationToken.Cancel();
//等待取消任務完成(重要)
task.Wait();
}
async Task ExecuteAsync()
{
Console.WriteLine("進入執行操做");
while (!cancellationToken.IsCancellationRequested)
{
//模擬長時間執行操做
await Task.Delay(50000);
}
Console.WriteLine("退出執行操做");
}
}
💡 因而可知,對於長時間異步任務的執行,當咱們想讓其中止時,咱們想固然的認爲只需調用取消令牌的Cancel方法就可萬事大吉,經練習實踐證實其實依然在執行,並無徹底中止!
💡 若利用Task.Run直接調用Wait方法便可,咱們也發現,當咱們須要作更多處理,使用Task.Factory.StartNew時,若對各類選項配置沒有一個很清楚的認識和理解,很容易用出毛病!
本文分享自微信公衆號 - JeffckyShare(JeffckyShare)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。