`

await Task 在Console应用程序和WPF,Metro中的不同

阅读更多

       在.NET 4.5中,新的异步机制非常好用,但是有一点很容易搞错,那就是一旦一个await 完成了,或者说是你的一个异步任务完成了,那么接下去由谁(哪个线程上下文)来直接执行之后的代码?在Wpf中(或在Metro中),当你进行await操作后,或者使用task.GetAwaiter()返回的TaskAwaiter 对象的OnCompleted(将你想要的后续操作放入它中),那么你会发现,它们都是在UI线程中执行的(使用Thread.CurrentThread.ManagedThreadId查看,但是Metro没有Thread,但这个比照WPF就行)。代码如下:

  private async void clicked(object sender, RoutedEventArgs e)
        {
            id0= Thread.CurrentThread.ManagedThreadId;
            await Go();
        }
        async Task Go()
        {
            var task = GetAnswerToLife();
            text.Text = text.Text +await task; 
        }
      
        async Task<int> GetAnswerToLife()
        {
            var task = Task.Delay(5000);
            var aw = task.GetAwaiter();
            aw.OnCompleted(() => 
            {
                var id1 = Thread.CurrentThread.ManagedThreadId;
            });
            await task; 
            int answer = 21 * 2;
            var id2=Thread.CurrentThread.ManagedThreadId;
          //  text.Text = text.Text +answer + "+"+id;
            return answer;
        }

 其中clicked是一个Button的Click事件,那么我们断点每个id处,会发现id0=id1=id2。

 

若是在控制台应用程序中,那么结果就很不一样了,我这里就不帖代码了,把以上这段代码移到控制台应用程序上很容易。你会发现,id0和id1,id2不一样,也就是id0是主线程的id,其他的id是其他线程的id,那么即使我在每个task上加上这段代码:

task.ConfigureAwait(true);

 

也还是没用(注意:若ConfigureAwait为true,在UI应用中,往往让调用该task的线程回来执行task完成后的代码,往往就是UI线程,若为false,则会让执行task具体任务的工作线程来执行。所以,当你把task.ConfigureAwait(false);添加到wpf的项目里时,你会发现,id0就不等于id1和id2了)。

 

那具体的原因是什么呢?突然发现,要完成以上的这种可以让一个线程有空时回到await处的前提条件是,该线程应该是一个基于消息循环的应用程序,这个是由编译器来将剩余代码打包成一个消息到消息队列中,所以普通的控制台应用程序没有这种机制,也就不可能让主线程回到它去过的地方执行了。参考如下的机制:

 

 

while (!thisApplication.Ended)
{
wait for something to appear in message queue
Got something: what kind of message is it?
Keyboard/mouse message -> fire an event handler
User BeginInvoke/Invoke message -> execute delegate
}

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics