C# 异步编程(GUI程序中的异步操作)

GUI程序中的异步操作

尽管本章目前的所有代码均针对控制台应用程序,但实际上异步方法在GUI程序中尤为

有用。

原因是GUI程序在设计上就要求所有的显示变化都必须在主GUI线程中完成,如点击按钮、

展示标签、移动窗体等。Windows程序是通过消息来实现这一点的,消息被放入由消息泵管理的

消息队列中。

消息泵从队列中取出一条消息,并调用它的处理程序(handler)代码。当处理程序代码完成

时,消息泵获取下一条消息并循环这个过程。

由于这种架构,处理程序代码就必须短小精悍六这样才不至于挂起并阻碍其他GUI行为的

处理。如果某个消息的处理程序代码耗时过长,消息队列中的消息会产生积压,程序将失去响应,

因为在那个长时间运行的处理程序完成之前,无法处理任何消息。

图21-11展示了一个WPF程序中两个版本的窗体。窗体由状态标签及其下方的按钮组成。

开发者的目的是,程序用户会点击按钮,而按钮的处理程序代码会执行以下操作:

  • 禁用按钮,这样在处理程序执行期间用户就不能再次点击了;
  • 将标签文本改为Doings懨这样用户就会知道程序正在工作;
  • 让程序休眠4秒钟一一一模拟某个工作;
  • 将标签文本改为原始文本,并启用按钮。
    图2-11中右侧的截屏展示了开发者希望在按钮按下的4秒之内窗体的样子。然而事实并非
    如此。当开发者点击按钮后,什么都没有发生。而且如果在点击按钮后移动窗体,会发现它已经
    冻结,不会移动一一一直到4秒之后,窗体才突然出现在新位置。

要使用Visual Studio重新创建这个名为MessagePump的WPF程序,步骤如下。

(1)选择File---New---Project菜单项,弹出NewProject窗口。

(2)在窗口左侧的面板内,展开lnstalled Templates(如果没有展开的话)。

(3)在C#类别中点击Windows条目,将在中间面板中弹出已安装的Windows Classic Desktop

程序模板。

(4)点击WPFApp(.NET框架),在窗口下方的Name文本框中输人MessagePumpo在其下方

选择一个位置,并点击OK按钮。

(5)将中的标记修改为下面的代码,在窗体中创建状态标签和按钮。

xml 复制代码
<Window x:Class="MessagePump.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
        Title="Pump" Height="120" Width="200" > 
    <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"/> 
    </StackPanel> 
</Window> 

(6)将代码隐藏文件MainWindow.xaml.cs修改为如下所示的c#代码。

csharp 复制代码
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
namespace MessagePump
{
    public partical class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

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

运行程序,你会发现其行为与之前的描述完全一致,即按钮没有禁用,状态标签也没有改变,

如果你移动窗体,4秒后它才会移动。

这个奇怪行为的原因其实非简单。图21-12展示了这种情形。点击按钮时,按钮的Click

消息放入消息队列。消息泵从队列中移除该消息并开始处理点击按钮的处理程序代码,即

btnDostuff_Click方法。btnDoStuff_Click处理程序将我们希望触发的行为的消息放人队列,如

右边的图所示。但在处理程序本身退出(即休眠4秒并退出)之前,这些消息都无法执行。然后

所有的行为都发生了,但速度太快,肉眼根本看不见。

消息泵分发消息队列中的消息。在按钮消息处理程序执行的时候,其他行

为的消息压入队列,但在其完成之前都无法执行

csharp 复制代码
private async void btnDoStuff_Click(object sender,RoutedEventArgs e)
{
    btnDoStuff_Click.IsEnabled = false;
    lblStatus.Content = "Doint Stuff";

    await Task.Delay(4000);

    lblStatus.Content = "Not Doing Anything";

    btnDoStuff_Click.IsEnabled = true;
}

Task.Yield

Task.Yield方法创建一个立即返回的awaitable。等待一个Yield可以让异步方法在执行后

续部分的同时返回到调用方法。可以将其理解成离开当前的消息队列,回到队列末尾,让处理器

有时间处理其他任务。

下面的示例代码展示了一个异步方法,程序每执行某个循环1000次就移交一次控制权。每

次执行方法,线程中的其他任务就得以执行。

csharp 复制代码
static class DoStuff
{
    public static async Task<int> FindSeriersSum(int i1)
    {
        int sum = 0;
        for (int i = 0; i < i1; i++)
        {
            sum += i;
            if (i % 1000 == 0)
                await Task.Yield();
        }
        return sum;
    }
}

class Program
{
    static void Main()
    {
        Task<int> value = DoStuff.FindSeriersSum(1_000_000);
        CountBig(100_000); CountBig(100_000);
        CountBig(100_000); CountBig(100_000);
        Console.WriteLine($"Sum:{value.Result}");
    }

    private static void CountBig(int p)
    {
        for (int i = 0; i < p; i++)
            ;
    }
}

Yield方法在GUI程序中非常有用,可以中断大量工作,让其他任务使用处理器。

相关推荐
咩?几秒前
SEABORN库函数(第十八节课内容总结)
开发语言·python·matplotlib·seaborn
C4程序员3 分钟前
北京JAVA基础面试30天打卡03
java·开发语言·面试
仪器科学与传感技术博士1 小时前
Matplotlib库:Python数据可视化的基石,发现它的美
开发语言·人工智能·python·算法·信息可视化·matplotlib·图表可视化
CHEN5_023 小时前
Java基础知识总结
java·开发语言
Kiri霧3 小时前
Kotlin反射
java·开发语言·kotlin
爱吃芒果的蘑菇4 小时前
使用pybind11封装C++API
开发语言·c++·python
慕y2744 小时前
Java学习第一百一十一部分——Jenkins(二)
java·开发语言·学习·jenkins
BUG再也不见4 小时前
Python爬虫 urllib 模块详细教程:零基础小白的入门指南
开发语言·网络·爬虫·python
weixin_307779135 小时前
C#实现Hive到Snowflake数据迁移
开发语言·数据仓库·hive·c#