Metro Style App 异步-简洁而不简单的异步
简洁的异步
在win8 中异步被大大的加强,在code 中占到相当大的比重,按照建议任何耗时超过0.5S的操作都应该使用异步,幸运的是在.NET 4.5中异步的开发相当简单
(明亮的类库都表示需要异步来操作,可见多么重要)
1 private bool test()
2
3 {
4
5 Method()
6
7 return true;
8
9 }
这是最原始的方法,当Method() 执行时间过长则会阻塞UI线程 ,在以前我们可能有多种写法来解决,但是在.NET 4.5中 这一切都被大大的简化了
只要在你需要的地方打上async 和await 关键字即可,async 必须在方法名和返回类型前
有async 则必须有await ,异步方法的返回类型必须是Task<T> 即 若要返回一个bool类型则方法声明
private async Task<bool> test()
{
await Method()
return true;
}
在这里要注意的是方法会等待Method 执行完毕后才会返回true, 但是不同的是不会阻塞UI线程 在test()方法内部可以操控UI元素,并且UI 不会假死,你需要注意的是这并不是什么神奇的语法,只是一些语法糖而已,犀利的编译器。
注意async不能用于 属性,构造函数,
异步的泥潭
对于win8 中的异步 在初学阶段我喜欢用泥潭这个词,有可能你会陷入一些奇怪的异步问题,因为在一些情况下 并不总是按照你想象的那样方式运行,可能会死锁 可能会没有等待方法完成就接着往下执行还有很多很多……这里给出几条个人建议
- 如果有 void 需要异步执行的方法则把void 换成Task
1 public async void btnWrite_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
2
3 {
4
5 WriteData ();
6
7 savedate();
8
9 }
10
11
12
13 private async void WriteData()
14
15 {
16
17 await Method()
18
19 }
我们执行这个btnWrite_Click的异步方法的时候会先进入WriteData() 中 按照上文所说的应该是等待WriteData 执行完毕后才会接着执行 savedate 方法,但是遗憾的是它并不会等待WriteData 执行完毕 就接着执行 savedate了 ,这是因为虽然WriteData 是个异步方法 但是这个异步是针对于 Method(),WriteData 本质是异步方法但是不需要等待 ,但是我们需要一个等待的异步…
public async void btnWrite_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
await WriteData ();
savedate();
}
private async Task WriteData()
{
await Method()
}
当我们把void 换成Task 时候 就明确的WriteData 定义为一个需要等待的异步方法 ,这样 btnWrite_Click 就会按照我们设想的那样会等待WriteData 执行完毕 才会执行savedate
- 用ConfigureAwait(false) 避免死锁
让我们来完善我们的文件读写,当写入文件失败的时候我们可能要记入log
public async void btnWrite_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
try
{
await WriteData();
btnWrite.Content = "ok";
}
catch (Exception)
{
WriteLog();
}
}
private async Task WriteData()
{
await Method()
}
private async Task WriteLog ()
{
await Method2()
}
但是遗憾的是在catch 中我们无法使用await 关键字,既然无法使用await ,那么对于异步方法 我们可以调用 WriteLog().Start() 来开始 ,但是我们无法确保WriteLog()什么时候执行完毕 ,也许在程序关闭了,还在写日志 .这显然不是我们所预期的, 或者我们可以尝试 WriteLog().GetAwaiter(); 这个主意不错可是当我们正在运行的时候会发现 程序死锁了,应为在WriteLog().GetAwaiter();
UI线程将会被阻塞 等待WriteLog方法执行完毕,返回控制权,在WriteLog内部会等待Method2()执行完毕 返回控制权.主意Method2 执行在其他线程(来自线程池) ,但是WriteLog则在UI线程执行… UI线程已经被阻塞,死锁就在Method2 执行完毕后发生.幸运的是我们还有办法解决 。
我们可以在WriteLog().ConfigureAwait(false) MSDN解释如下
- Task.ConfigureAwait(bool continueOnCapturedContext)
- true (default)
- Post continuation back to the current context/scheduler
- false
- If possible, continue executing where awaited task completes
- true (default)
个人觉得和Task.Yield(); 很相似 .但是Task.Yield();有不确定性 并且需要await 支持。