当前位置: 首页 > news >正文

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

GUI程序中的异步操作

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

原因是GUI程序在设计上就要求所有的显示变化都必须在主GUI线程中完成,如点击按钮、
展示标签、移动窗体等。Windows程序是通过消息来实现这一点的,消息被放入由消息泵管理的
消息队列中。

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

由于这种架构,处理程序代码就必须短小精悍六这样才不至于挂起并阻碍其他GUI行为的
处理。如果某个消息的处理程序代码耗时过长,消息队列中的消息会产生积压,程序将失去响应,
因为在那个长时间运行的处理程序完成之前,无法处理任何消息。
图21-11展示了一个WPF程序中两个版本的窗体。窗体由状态标签及其下方的按钮组成。
开发者的目的是,程序用户会点击按钮,而按钮的处理程序代码会执行以下操作:

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

包含一个按钮和一个状态字符串的简单WPF程序

要使用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)将中的标记修改为下面的代码,在窗体中创建状态标签和按钮。

<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#代码。

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秒并退出)之前,这些消息都无法执行。然后
所有的行为都发生了,但速度太快,肉眼根本看不见。

image
消息泵分发消息队列中的消息。在按钮消息处理程序执行的时候,其他行
为的消息压入队列,但在其完成之前都无法执行

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次就移交一次控制权。每
次执行方法,线程中的其他任务就得以执行。

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程序中非常有用,可以中断大量工作,让其他任务使用处理器。

http://www.lryc.cn/news/613972.html

相关文章:

  • 从浅拷贝到深拷贝:C++赋值运算符重载的核心技术
  • 【设计模式】抽象工厂模式 (工具(Kit)模式)
  • 【接口自动化】-2- request模块及通过变量实现接口关联
  • 瑞利杂波背景下不同环境的虚警概率与目标检测概率仿真
  • 项目历程—右键菜单(问题,解决,拓展(非教学向,因为乱))
  • django uwsgi启动报错failed to get the Python codec of the filesystem encoding
  • 17.14 CogVLM-17B多模态模型爆肝部署:4-bit量化+1120px高清输入,A100实战避坑指南
  • 流形折叠与条件机制
  • 【ee类保研面试】其他类---计算机网络
  • STM32HAL 快速入门(二):用 CubeMX 配置点灯程序 —— 从工程生成到 LED 闪烁
  • 如何在Vue中使用拓扑图功能
  • 相机坐标系与世界坐标系的点相互转换:原理、可视化与实践
  • HTML 与 CSS:从 “认识标签” 到 “美化页面” 的入门指南
  • Numpy科学计算与数据分析:Numpy数据分析与图像处理入门
  • 使用Python提取PDF大纲(书签)完整指南
  • Date、Calendar、LocalDateTime:Java 处理时间的类该怎么选?
  • 【网络自动化】利用Python脚本与计划任务,实现H3C/HPE设备配置无人值守备份
  • 安装向量数据库chromadb
  • Java+uniapp+websocket实现实时聊天,并保存聊天记录
  • mac笔记本如何重新设置ssh key
  • React Hooks 完全指南:从概念到内置 Hooks 全解析
  • 五种IO模型与⾮阻塞IO
  • leetcode1456:定长子串中元音的最大数目(定长滑动窗口)
  • 云平台运维工具 —— 阿里云原生工具
  • 云原生时代的 Linux:容器、虚拟化与分布式的基石
  • react的form.resetFields()
  • 人工智能之数学基础:事件独立性
  • Java中重写和重载有哪些区别
  • MySQL vs PostgreSQL 深度对比:为你的新项目选择正确的开源数据库 (2025)
  • LVS高可靠