20191123
今天学了一些关于异步编程的知识点,现总结记录以下关键点:
1、报告进度
在异步操作过程,展示操作的进度
使用IProgress<T>和Progress<T>。在async方法里传入IProgress<T>,其中T是需要报告的进度类型。
注意:由于IProgress<T>.Report方法是可以移步的,意味着报告进度之前,异步方法会继续运行,因此最好将T定义为一个不可变类型(至少是值类型,引用类型的话需要在调用IProgress<T>.Report时创建一个单独副本)
2 、Task.WhenAll
输入多个任务,完成后返回一个完成的Task对象,异常时会放在返回的Task里,多个异常也会如此。但在await调用时,只抛出一个异常。
3、任务完成时的处理
A
var processingTasks = (from t in tasks select AwaitAsync(t).toArray())
await Task.WhenAll(processingTasks);
B
扩展方法:OrderByCompletion
foreach (var task in tasks.OrderByCompletion())
{
var resulr = await task;
Trace.WriteLine(result);
}
4、避免线程上下文延续
async方法在被await调用后恢复运行时,会在原上下文中运行,如果是UI上下文,可能会有大量的async方法需要在UI上下文中恢复。
为避免在上下文中恢复,可以通过await调用ConfigureAwait()方法的返回值,将参数continueOnCaptureContext=false,
使代码一开始在调用的线程里执行,当被await暂停后,转到线程池线程里运行
大部分I/O型任务采用 TaskCompletionSource.
UI线程中有多少延续任务算多,winRT团队的指导标准,100个/s。
推荐:核心库一直使用ConfigureAwait,在外围用户界面中,只在需要时恢复上下文。
如果一部分需要使用,一部分不需要,建议拆分成多个async方法。
5、async异常
async Task方法引发的异常,存放在返回的Task中,只有当Task对象被await调用时,才会抛出(重新抛出)
await重新抛出的异常,原始的栈轨迹会被正确保存。返回的Task可能会有多个异常,但await智慧抛出一个。需要获取多个异常,需要根据Task的exception属性进行判断。