Code Bye

async/await的疑问

        private Task<string> doAsyncWork()
{
return Task.Run(() =>
{
Thread.Sleep(10000);
return “done”;
});
}
private async void btn_test_Click(object sender, RoutedEventArgs e)
{
try
{
btn_test.Content = await doAsyncWork();
MessageBox.Show(“haha”);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
上面代码的运行结果是:点击按钮后,等待10秒,然后按钮的文字变成done,接着弹出对话框haha,但是神奇的是,整个过程中,包括等待的10秒过程中,界面都是可以响应的!请大家看看下面这两句:
btn_test.Content = await doAsyncWork();
MessageBox.Show(“haha”);
第一句表示调用线程A(也就是UI线程)开启了一个线程B,B在等待10秒,此时A并没有输出haha,说明A是阻塞在第一句的,但为什么这10秒过程中,UI都能响应?A不是被阻塞挂起了吗?
解决方案

50

await 就是异步回调。也就是相当于
                btn_test.Content = doCallback(delegate
                {
                           ...............这里是 await 之前的代码
                           doAsyncWork();  
                },
               resultValue =>
               {
                      ..........这里是await之后的代码
                      MessageBox.Show("haha");

});
这里所谓的“用顺序流程的语法来写一个异步多线程程序”是个障眼法,它其实是专门制造假象的。
实际上,btn_test_Click 方法在执行到 await 的地方就已经结束了,后边的都是回调执行代码。所以语法 async 放到方法修饰中,不让你调试到 btn_test_Click 方法结束部分。以此让程序员看不清楚回调的本意。

50

这是原因是编译器做了手脚。btn_test_Click由编译器重组为相似如下代码:
private void btn_test_Click(object sender, RoutedEventArgs e)
{
    Task<string> t = doAsyncWork();
    t.ContinueWith(WhenTaskFinished, TaskScheduler.FromCurrentSynchronizationContext());  // 1,2
}  // 3
private void WhenTaskFinished(Task<string> task) // 4
{
    try
    {
        btn_test.Content = task.Result;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.ToString());
    }
}

你可以观察到:
1、await就是一种ContinueWith,它登记一个’回调函数‘,这个回调函数(即那个WhenTaskFinished)将在Task完成后得到调用。
2、await同时也捕获了当前的同步环境,即那个TaskScheduler.FromCurrentSynchronizationContext,
并只在捕获的同步环境下运行’回调函数‘。
3、await马上返回。
原因是await启动task后马上返回UI,因此UI不会被阻塞。
原因是await捕获的同步环境就是UI,因此回调将在UI线程上执行,而不用麻烦Dispatcher.Invoke来同步到UI。


CodeBye 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权 , 转载请注明async/await的疑问