Metro Style App 异步-简洁而不简单的异步

 

简洁的异步

在win8 中异步被大大的加强,在code 中占到相当大的比重,按照建议任何耗时超过0.5S的操作都应该使用异步,幸运的是在.NET 4.5中异步的开发相当简单

 

(明亮的类库都表示需要异步来操作,可见多么重要)

 

 

  

View Code
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 中的异步 在初学阶段我喜欢用泥潭这个词,有可能你会陷入一些奇怪的异步问题,因为在一些情况下 并不总是按照你想象的那样方式运行,可能会死锁 可能会没有等待方法完成就接着往下执行还有很多很多……这里给出几条个人建议

  1. 如果有  void 需要异步执行的方法则把void 换成Task

    

View Code
 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  本质是异步方法但是不需要等待 ,但是我们需要一个等待的异步…

View Code
        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

 

  1. 用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

个人觉得和Task.Yield();  很相似 .但是Task.Yield();有不确定性 并且需要await 支持。

 

 

 posted on 2012-03-09 18:54  沐訫  阅读(2420)  评论(2编辑  收藏  举报