C#中的异步多线程7 WPF中的异步操作

实际上异步方法在GUI程序中尤为有用,GUI程序在设计上就要求所有的现实变化都必须在GUI主线程上完成,比如点击按钮,展示标签,移动窗体等。Windows程序通过消息来实现这一点,消息会被放入由消息泵管理的消息队列中。

消息泵从消息队列中取出一条信息,并调用它的处理程序(handler)代码。当处理程序代码完成时,消息泵获取下一条消息并循环这个过程。

因为这种机制,处理程序就必须短小精悍,才不至于挂起并阻碍其他GUI行为的处理。如果某个消息的处理程序代码耗时过长,消息队列中的消息就会产生挤压,程序会失去响应,因为在哪个长时间运行的处理程序完成之前,无法处理其他任何消息。

为了模拟一个长耗时的任务造成的影响,建立如下的WPF程序:

<Window x:Class="MessagePump_AsyncSample.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <StackPanel>
        <Label Name="lblStatus" Margin="10,5,10,0">Not Doing Anything</Label>
        <Button Name="btnDoStuff" Content="Do stuff" HorizontalAlignment="Left" Margin="10,5" Padding="5,2" Click="btnDoStuff_Click"></Button>
    </StackPanel>
</Window>
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void btnDoStuff_Click(object sender, RoutedEventArgs e)
        {
            btnDoStuff.IsEnabled = false;
            lblStatus.Content = "Doing Stuff";
            Thread.Sleep(4000);
            //await Task.Delay(4000);
            lblStatus.Content = "Not Doing Anything";
            btnDoStuff.IsEnabled = true;
        }

用户点击按钮后:禁用按钮——将标签文本修改为Doing Stuff,这样用户会知道程序在运行——休眠4s,模拟长耗时任务——将标签改为原始文本,并启用按钮。

但实际上,点击按钮后,什么也没有发生,4s内也不能移动窗体,4s后窗体才会出现在新位置。

原因在于,点击按钮哦是,click消息放入消息队列,消息泵从队列中移除该信息并开始处理点击按钮的处理程序代码,即btnDoStuff_Click方法中的代码,btnDoStuff_Click将希望发生的行为放入队列(禁用按钮,改变文本,移动窗体,改变文本,恢复按钮),但在处理程序本身退出之前,这些消息都无法执行(4s),然后所有的行为都发生了,但速度太快看不到。

但如果,处理程序能将前2条消息压入队列,然后将自己从处理器上摘下,4s后再压入队列,那么这些消息都可以在等待的时间内被处理,还能保持响应。

async,await就可以做到,当到达await关键字时,处理程序返回到调用方法,并从处理器上摘下,这时其他消息得到,包括最初的2条记录,在空闲任务(Task.Delay)完成后,后续部分又重新被安排到线程上。

原文地址:https://www.cnblogs.com/NicolasLiaoran/p/12939486.html