通过上面的图,可以了解到有SynchronizationContext和没有SynchronizationContext环境的不同,是否恢复SynchronizationContext的影响。对于ASP.NET环境虽然也有SynchronizationContext,但实测线程切换的表现比较诡异,实在无法具体分析,但按照WPF的方式来配置异步肯定是对的。
其它资料:据CLR via C#作者大神Jeffrey Richter在书中所说,.NET这种以状态机实现异步的思想来自于其为.NET 4.0写的Power Threading库中的AsyncEnumerator类。可以将其作为一个参考来学习async异步方法的机制。
async异步编程中的取消和进度报告
由文章开始处的图1可知,Task天生支持取消,通过一个接收CancellationToken的重载创建的Task可以被通知取消。
var tokenSource = new CancellationTokenSource(); CancellationToken ct = tokenSource.Token; var task = Task.Run(() => Task.Delay(10000,ct), ct); tokenSource.Cancel();自然我们异步方法的取消也离不开CancellationToken,方法就是给异步方法添加接收CancellationToken的重载,如前文示例代码Service中的方法可以添加一个这样的重载支持取消:
public async Task<byte[]> GetUserAvatarAsync(int id, CancellationToken ct) { ... }async异步编程最大的一个特点就是传播性,即如果有一个异步方法,则所有调用这个方法的方法都应该是异步方法,而不能有任何同步方法(控制台应用Main函数中那种把异步转同步的方式除外)。而通过CancellationToken实现的取消模式可以很好的适配这种传播性,所需要做的就是把所有异步方法都添加支持CancellationToken的重载。之前的例子改造成支持取消后如下(展示一部分):
class Program { static void Main(string[] args) { var tokenSource = new CancellationTokenSource(); CancellationToken ct = tokenSource.Token; var userService = new Service(); var avatar = userService.GetUserAvatarAsync(1,ct).Result; tokenSource.Cancel(); Console.Read(); } } public class Service { private readonly Repository _repository; private readonly WebHepler _webHelpler; public Service() { _repository = new Repository(); _webHelpler = new WebHepler(); } public async Task<byte[]> GetUserAvatarAsync(int id, CancellationToken ct) { var user = await _repository.GetByIdAsync(id, ct); var email = user.Email; ct.ThrowIfCancellationRequested(); var avatar = await _webHelpler.GetAvatarByEmailAsync(email, ct); return avatar; } }注意ct.ThrowIfCancellationRequested()调用,这是可以及时取消后续未完成代码的关键。当执行这个语句时,如果ct被标记取消,则这个语句抛出OperationCanceledException异常,后续代码停止执行。