1、走进异步编程的世界在WinForm中执行异步操作概要走进异步编程的世界 -在 WinForm 中执行异步操作序这是继开始接触 async/await 异步编程、走进异步编程的世界 - 剖析异步方法后的第三篇。主要介绍在 WinForm 中如何执行异步操作。 在 WinForm 中执行异步操作 在 WinForm 中使用异步 Lambda 表达式 一个完整的 WinForm 程序 另一种异步方式 - BackgroundWorker 类一、在 WinForm 程序中执行异步操作下面通过窗体示例演示以下操作-点击按钮后:将按钮禁用,并将标签内容改成:“Doing”(表示执行中);线程挂起3秒(模拟
2、耗时操作);启用按钮,将标签内容改为:“Complete”(表示执行完成)。1 public partial class Form1 : Form 2 3 public Form1() 4 5 InitializeComponent(); 6 7 8 private void btnDo_Click(object sender, EventArgs e) 9 10 btnDo.Enabled = false;11 lblText.Text = Doing;12 13 Thread.Sleep(3000);14 15 btnDo.Enabled = true;16 lblText.Text =
3、Complete;17 18 可是执行结果却是:图1-1【发现的问题】好像没有变成“Doing”?并且拖动窗口的时候卡住不动了?3秒后突然变到想拖动到的位置?同时文本变成“Complete”?【分析】GUI 程序在设计中要求所有的显示变化都必须在主 GUI 线程中完成,如点击事件和移动窗体。Windows程序时通过 消息来实现,消息放入消息泵管理的消息队列中。点击按钮时,按钮的Click消息放入消息队列。消息泵从队列中移除该消息,并开始处理点击事件的代码,即btnDo_Click 事件的代码。btnDo_Click 事件会将触发行为的消息放入队列,但在btnDo_Click 时间处理程序完全退
4、出前(线程挂起 3 秒退出前),消息都无法执行。(3 秒后)接着所有行为都发生了,但速度太快肉眼无法分辨才没有发现标签改成“Doing”。图1-2 点击事件图1-3 点击事件具体执行过程现在我们加入 async/await 特性。1 public partial class Form1 : Form 2 3 public Form1() 4 5 InitializeComponent(); 6 7 8 private async void btnDo_Click(object sender, EventArgs e) 9 10 btnDo.Enabled = false;11 lblText.
5、Text = Doing;12 13 await Task.Delay(3000);14 15 btnDo.Enabled = true;16 lblText.Text = Complete;17 18 图1-4现在,就是原先希望看到的效果。【分析】btnDo_Click 事件处理程序先将前两条消息压入队列,然后将自己从处理器移出,在3秒后(等待空闲任务完成后 Task.Delay )再将自己压入队列。这样可以保持响应,并保证所有的消息可以在线程挂起的时间内被处理。1.1 Task.YieldTask.Yield 方法创建一个立刻返回的 awaitable。等待一个Yield可以让异步方法在执
6、行后续部分的同时返回到调用方法。可以将其理解为 离开当前消息队列,回到队列末尾,让 CPU 有时间处理其它任务。1 class Program 2 3 static void Main(string args) 4 5 const int num = 1000000; 6 var t = DoStuff.Yield1000(num); 7 8 Loop(num / 10); 9 Loop(num / 10);10 Loop(num / 10);11 12 Console.WriteLine($Sum: t.Result);13 14 Console.Read();15 16 17 / 18 /
7、 循环19 / 20 / 21 private static void Loop(int num)22 23 for (var i = 0; i num; i+) ;24 25 26 27 internal static class DoStuff28 29 public static async Task Yield1000(int n)30 31 var sum = 0;32 for (int i = 0; i 9 10 Do(false, Doing);11 12 await Task.Delay(3000);13 14 Do(true, Finished);15 ;16 17 18 p
8、rivate void Do(bool isEnable, string text)19 20 btnDo.Enabled = isEnable;21 lblText.Text = text;22 23 还是原来的配方,还是熟悉的味道,还是原来哪个窗口,变的只是内涵。图2-1三、一个完整的 WinForm 程序现在在原来的基础上添加了进度条,以及取消按钮。1 public partial class Form1 : Form 2 3 private CancellationTokenSource _source; 4 private CancellationToken _token; 5 6
9、public Form1() 7 8 InitializeComponent(); 9 10 11 / 12 / Do 按钮事件13 / 14 / 15 / 16 private async void btnDo_Click(object sender, EventArgs e)17 18 btnDo.Enabled = false;19 20 _source = new CancellationTokenSource();21 _token = _source.Token;22 23 var completedPercent = 0; /完成百分比24 const int time = 10
10、; /循环次数25 const int timePercent = 100 / time; /进度条每次增加的进度值26 27 for (var i = 0; i time; i+)28 29 if (_token.IsCancellationRequested)30 31 break;32 33 34 try35 36 await Task.Delay(500, _token);37 completedPercent = (i + 1) * timePercent;38 39 catch (Exception)40 41 completedPercent = i * timePercent;
11、42 43 finally44 45 progressBar.Value = completedPercent;46 47 48 49 var msg = _token.IsCancellationRequested ? $进度为:completedPercent% 已被取消! : $已经完成;50 51 MessageBox.Show(msg, 信息);52 53 progressBar.Value = 0;54 InitTool();55 56 57 / 58 / 初始化窗体的工具控件59 / 60 private void InitTool()61 62 progressBar.Valu
12、e = 0;63 btnDo.Enabled = true;64 btnCancel.Enabled = true;65 66 67 / 68 / 取消事件69 / 70 / 71 / 72 private void btnCancel_Click(object sender, EventArgs e)73 74 if (btnDo.Enabled) return;75 76 btnCancel.Enabled = false;77 _source.Cancel();78 79 图3-1四、另一种异步方式 - BackgroundWorker 类与 async/await 不同的是,你有时候可能需要一个额外的线程,在后台持续完成某项任务,并不时与主线程通信,这时就需要用到BackgroundW
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1