走进异步编程的世界在WinForm中执行异步操作概要.docx
《走进异步编程的世界在WinForm中执行异步操作概要.docx》由会员分享,可在线阅读,更多相关《走进异步编程的世界在WinForm中执行异步操作概要.docx(14页珍藏版)》请在冰豆网上搜索。
走进异步编程的世界在WinForm中执行异步操作概要
走进异步编程的世界- 在WinForm中执行异步操作
序
这是继《开始接触async/await异步编程》、《走进异步编程的世界-剖析异步方法》后的第三篇。
主要介绍在WinForm中如何执行异步操作。
•在WinForm中执行异步操作
•在WinForm中使用异步Lambda表达式
•一个完整的WinForm程序
•另一种异步方式-BackgroundWorker类
一、在WinForm程序中执行异步操作
下面通过窗体示例演示以下操作-点击按钮后:
①将按钮禁用,并将标签内容改成:
“Doing”(表示执行中);
②线程挂起3秒(模拟耗时操作);
③启用按钮,将标签内容改为:
“Complete”(表示执行完成)。
1publicpartialclassForm1:
Form
2{
3publicForm1()
4{
5InitializeComponent();
6}
7
8privatevoidbtnDo_Click(objectsender,EventArgse)
9{
10btnDo.Enabled=false;
11lblText.Text=@"Doing";
12
13Thread.Sleep(3000);
14
15btnDo.Enabled=true;
16lblText.Text=@"Complete";
17}
18}
可是执行结果却是:
图1-1
【发现的问题】
①好像没有变成“Doing”?
②并且拖动窗口的时候卡住不动了?
③3秒后突然变到想拖动到的位置?
④同时文本变成“Complete”?
【分析】GUI程序在设计中要求所有的显示变化都必须在主GUI线程中完成,如点击事件和移动窗体。
Windows 程序时通过消息来实现,消息放入消息泵管理的消息队列中。
点击按钮时,按钮的Click消息放入消息队列。
消息泵从队列中移除该消息,并开始处理点击事件的代码,即 btnDo_Click事件的代码。
btnDo_Click事件会将触发行为的消息放入队列,但在 btnDo_Click时间处理程序完全退出前(线程挂起3秒退出前),消息都无法执行。
(3秒后)接着所有行为都发生了,但速度太快肉眼无法分辨才没有发现标签改成“Doing”。
图1-2点击事件
图1-3点击事件具体执行过程
现在我们加入async/await特性。
1publicpartialclassForm1:
Form
2{
3publicForm1()
4{
5InitializeComponent();
6}
7
8privateasyncvoidbtnDo_Click(objectsender,EventArgse)
9{
10btnDo.Enabled=false;
11lblText.Text=@"Doing";
12
13awaitTask.Delay(3000);
14
15btnDo.Enabled=true;
16lblText.Text=@"Complete";
17}
18}
图1-4
现在,就是原先希望看到的效果。
【分析】btnDo_Click事件处理程序先将前两条消息压入队列,然后将自己从处理器移出,在3秒后(等待空闲任务完成后Task.Delay)再将自己压入队列。
这样可以保持响应,并保证所有的消息可以在线程挂起的时间内被处理。
1.1Task.Yield
Task.Yield方法创建一个立刻返回的awaitable。
等待一个Yield可以让异步方法在执行后续部分的同时返回到调用方法。
可以将其理解为离开当前消息队列,回到队列末尾,让CPU有时间处理其它任务。
1classProgram
2{
3staticvoidMain(string[]args)
4{
5constintnum=1000000;
6vart=DoStuff.Yield1000(num);
7
8Loop(num/10);
9Loop(num/10);
10Loop(num/10);
11
12Console.WriteLine($"Sum:
{t.Result}");
13
14Console.Read();
15}
16
17///
18///循环
19///
20///
21privatestaticvoidLoop(intnum)
22{
23for(vari=0;i24}
25}
26
27internalstaticclassDoStuff
28{
29publicstaticasyncTaskYield1000(intn)
30{
31varsum=0;
32for(inti=0;i33{
34sum+=i;
35if(i%1000==0)
36{
37awaitTask.Yield();//创建异步产生当前上下文的等待任务
38}
39}
40
41returnsum;
42}
43}
图1.1-1
上述代码每执行1000次循环就调用Task.Yield方法创建一个等待任务,让处理器有时间处理其它任务。
该方法在GUI程序中是比较有用的。
二、在WinForm中使用异步Lambda表达式
将刚才的窗口程序的点击事件稍微改动一下。
1publicpartialclassForm1:
Form
2{
3publicForm1()
4{
5InitializeComponent();
6
7//async(sender,e)异步表达式
8btnDo.Click+=async(sender,e)=>
9{
10Do(false,"Doing");
11
12awaitTask.Delay(3000);
13
14Do(true,"Finished");
15};
16}
17
18privatevoidDo(boolisEnable,stringtext)
19{
20btnDo.Enabled=isEnable;
21lblText.Text=text;
22}
23}
还是原来的配方,还是熟悉的味道,还是原来哪个窗口,变的只是内涵。
图2-1
三、一个完整的WinForm程序
现在在原来的基础上添加了进度条,以及取消按钮。
1publicpartialclassForm1:
Form
2{
3privateCancellationTokenSource_source;
4privateCancellationToken_token;
5
6publicForm1()
7{
8InitializeComponent();
9}
10
11///
12///Do按钮事件
13///
14///
15///
16privateasyncvoidbtnDo_Click(objectsender,EventArgse)
17{
18btnDo.Enabled=false;
19
20_source=newCancellationTokenSource();
21_token=_source.Token;
22
23varcompletedPercent=0;//完成百分比
24constinttime=10;//循环次数
25constinttimePercent=100/time;//进度条每次增加的进度值
26
27for(vari=0;i