你的位置:首页 > ASP.net教程

[ASP.net教程][C#] 在 GUI 中执行异步操作(更新于09


在 GUI 中执行异步操作

 

 

目录

 

一、在 GUI 程序中执行异步操作

  下面通过窗体示例演示以下操作-点击按钮后:①将标签内容改成:“Doing”,并将按钮禁用(表示执行中);②线程挂起3秒(模拟耗时操作);③启用按钮,将标签内容改为:“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 = @"Complete";17     }18   }

  可是执行结果却是:

图1-1

 

  发现的问题:好像没有变成“Doing”,并且拖动窗口的时候卡住不动了,3秒后突然变到想拖动到的位置,并且文本变成“Complete”?

  【分析】GUI 程序在设计中要求所有的显示变化都必须在主 GUI 线程中完成,如点击事件和移动窗体。Windows 程序时通过 消息来实现,消息放入消息泵管理的消息队列中。点击按钮时,按钮的Click消息放入消息队列。消息泵从队列中移除该消息,并开始处理点击事件的代码,即 btnDo_Click 事件的代码。btnDo_Click 事件会将触发行为的消息放入队列,但在 btnDo_Click 时间处理程序退出前(线程挂起 3 秒退出前),消息都无法执行。接着所有行为都发生了,但速度太快肉眼分辨不了。

图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.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.Yield

  Task.Yield 方法创建一个立刻返回的 awaitable。等待一个Yield可以让异步方法在执行后续部分的同时返回到调用方法。可以将其理解为 离开当前消息队列,回到队列末尾,让 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     /// <summary>18     /// 循环19     /// </summary>20     /// <param name="num"></param>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<int> Yield1000(int n)30     {31       var sum = 0;32       for (int i = 0; i < n; i++)33       {34         sum += i;35         if (i % 1000 == 0)36         {37           await Task.Yield(); //创建异步产生当前上下文的等待任务38         }39       }40 41       return sum;42     }43   }

 图1.1-1

  上述代码每执行1000次循环就调用 Task.Yield 方法创建一个等待任务,让处理器有时间处理其它任务。该方法在 GUI 程序中非常有用。

 


【原文地址】http://www.cnblogs.com/liqingwen/p/5877042.html 

 ========预览版,整理完毕后发布到首页=========