读书人

await Task 在Console应用程序跟WPF

发布时间: 2012-12-21 12:03:49 作者: rapoo

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 queueGot something: what kind of message is it?Keyboard/mouse message -> fire an event handlerUser BeginInvoke/Invoke message -> execute delegate}

?

读书人网 >网络基础

热点推荐