博客园首发-自定义awaiter
假设阅读本文的您已经对await/async有了一定了解。
.net中的新关键字await出来很久,刚接触时心里对它非常抵触,习惯了用过去的异步模型,感觉已经足够我实现各种想要的功能,认为新出的await简化了代码,必然牺牲代码的灵活性。这段时间公司项目不忙,周围同事一起讨论await用法,讨论之间发现大家对其理解各不相同,这……激发我对await的深入研究的兴趣。
首先说几点常见的误区,随后介绍下await的高级用法。
误区一:带有async/await关键字的方法难道就是异步方法了吗?
答案是:不是,有async关键字只是告诉编译器我这个方法可以用await。实际上没发起任何异步操作,
即使再有await也不是用来发起异步操作的。await仅仅是一个高级的handler。
误区二: 我们通常只用await后跟一个方法例如:
await SomeMethod();
await不是用来调用方法的前缀。它是用来接收一个异步的返回值例如Task;
可以这样写:
var result=SomeMethod();
await result;
误区三:await的控制流:微软做了大量的努力通过await/async关键字,把之前的前后颠倒,跳来跳去的控制流串成我们代码所写的顺序,
为了增强阅读性,让我们易于调试和开发。很多人(包括之前的我)真的以为带有async的函数就是一个线程的顺序执行的控制流。
实际上不是那样。副一张图介绍下。
实线箭头为主线程的控制流,虚线箭头为接收异步回调开启的新线程的控制流。再看一个例子。
现在我们避免了上面的误区可以更自由的发挥了。
最后一个介绍才是本文的重头戏:
await的高级应用,自定义awaiter。让我们的await可以await一切。
之前我们的await只能这样用的:
await SomeMethod();
await Task.delay(5000);
或者
var result=SomeMethod();
await result;
大家有没有这样用过?
await 5000;//延迟5秒;等于awaitTask.delay(5000);
string s=await "http://www.hanselman.com/blog/NuGetPackageOfTheWeek13PortableHttpClientMakesPortableLibrariesMoreUseful.aspx";//get这个地址的html;
甚至可以这样
Task.Run(()=>{
string s="hello";
this.SomeTextBlock.Text=s;
});
上面的写法肯定是会报错的,因为不能从其他线程给UI线程的控件赋值。
我自己封装了一个线程同步的方法:
Task.Run(()=>{
string s="hello";
await this.SomeTextBlock;
this.SomeTextBlock.Text=s;
});
await一下这个想要被操作的控件就可以赋值了。
是不是很过瘾?
我来一步一步告诉大家怎么样实现。
await接收的是TaskAwaiter 或TaskAwaiter <T> 的返回值 所以我们定义一个扩展方法可以让await当做自定义类型的前缀。
public static class AwaitHelper { //public static TaskAwaiter GetAwaiter(this TimeSpan timeSpan) //{ // return TaskEx.Delay(timeSpan).GetAwaiter(); //} public static TaskAwaiter GetAwaiter(this int ms) { return Task.Delay(ms).GetAwaiter(); } public static TaskAwaiter GetAwaiter(this string s) { return new Task(() => { }).GetAwaiter(); } public static AllanControlAwaiter GetAwaiter(this DependencyObject control) { return new AllanControlAwaiter(control); } }
看到这里我们就知道
await 5000;//延迟5秒;
是怎么实现的吧。
下面我们继续封装一个AllanControlAwaiter实现线程同步操作
public class AllanControlAwaiter : INotifyCompletion { private readonly DependencyObject m_control; private bool _IsCompleted = false; public AllanControlAwaiter(DependencyObject control) { m_control = control; } public bool IsCompleted { get { return _IsCompleted; } } public void OnCompleted(Action continuation) { var result = m_control.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => continuation()); result.AsTask().Wait(); _IsCompleted = true; } public void GetResult() { } }
到这里大功告成。
快去体验一下可以await一切的感觉吧!