【导读】目前私下空余时间在研究远程大文件断点续传下载,正在进行时,正当研究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

到了这里,想必咱们已经清楚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源创计划”,欢迎正在阅读的你也加入,一块儿分享。