winform什么时候会调用closed事件_async/await 给程序带来了什么?
如果說async給ASP.NET帶來的是處理能力的提高,那么在WinForm中給程序員帶來的好處則是最大的。我們再也不用因?yàn)橐獙?shí)現(xiàn)異步寫回調(diào)或者綁定事件了,省事了,可讀性也提高了。不信你看下面我們將調(diào)用我們那個(gè)web service的代碼在.NET4.5下實(shí)現(xiàn)一下:
1 2 3 4 5 6 7 | private?async?void?button2_Click(object?sender, EventArgs e) { ????var?pageContent =?new?localhost.PageContentSoapClient(); ????var?content = await pageContent.DownloadContentAsync("http://jesse2013.cnblogs.com"); ????textBox1.Text = content.Body.DownloadContentResult; } |
簡單的三行代碼,像寫同步代碼一樣寫異步代碼,我想也許這就是async/await的魔力吧。在await之后,UI線程就可以回去響應(yīng)UI了,在上面的代碼中我們是沒有新線程產(chǎn)生的,和EAP一樣拿到結(jié)果直接就可以對UI操作了。
async/await似乎真的很好,但是如果我們await后面的代碼執(zhí)行在另外一個(gè)線程中會(huì)發(fā)生什么事情呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | private?async?void?button1_Click(object?sender, EventArgs e) { ????label1.Text =?"Calculating Sqrt of 5000000"; ????button1.Enabled =?false; ????progressBar1.Visible =?true; ????double?sqrt = await Task<double>.Run(() => ????{ ????????double?result = 0; ????????for?(int?i = 0; i < 50000000; i++) ????????{ ????????????result += Math.Sqrt(i); ????????????progressBar1.Maximum = 50000000; ????????????progressBar1.Value = i; ????????} ????????return?result; ????}); ????progressBar1.Visible =?false; ????button1.Enabled =?true; ????label1.Text =?"The sqrt of 50000000 is "?+ sqrt; } |
我們在界面中放了一個(gè)ProgressBar,同時(shí)開一個(gè)線程去把從1到5000000的平方全部加起來,看起來是一個(gè)非常耗時(shí)的操作,于是我們用Task.Run開了一個(gè)新的線程去執(zhí)行。(注:如果是純運(yùn)算的操作,多線程操作對性能沒有多大幫助,我們這里主要是想給UI一個(gè)進(jìn)度顯示當(dāng)前進(jìn)行到哪一步了。)看起來沒有什么問題,我們按F5運(yùn)行吧!
Bomb~
當(dāng)執(zhí)行到這里的時(shí)候,程序就崩潰了,告訴我們”無效操作,只能從創(chuàng)建porgressBar的線程訪問它。“ ?這也是我們一開始提到的,在WinForm程序中,只有UI主線程才能對UI進(jìn)行操作,其它的線程是沒有權(quán)限的。接下來我們就來看看,如果在WinForm中實(shí)現(xiàn)非UI線程對UI控制的更新操作。?
萬能的Invoke
WinForm中絕大多數(shù)的控件包括窗體在內(nèi)都實(shí)現(xiàn)了Invoke方法,可以傳入一個(gè)Delegate,這個(gè)Delegate將會(huì)被擁有那個(gè)控制的線程所調(diào)用,從而避免了跨線程訪問的問題。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | Trace.TraceInformation("UI Thread : {0}", Thread.CurrentThread.ManagedThreadId); double?sqrt = await Task<double>.Run(() => { ????Trace.TraceInformation("Run calculation on thread: {0}", Thread.CurrentThread.ManagedThreadId); ????double?result = 0; ????for?(int?i = 0; i < 50000000; i++) ????{ ????????result += Math.Sqrt(i); ????????progressBar1.Invoke(new?Action(() => { ????????????Trace.TraceInformation("Update UI on thread: {0}", Thread.CurrentThread.ManagedThreadId); ????????????progressBar1.Maximum = 50000000; ????????????progressBar1.Value = i; ????????})); ????} ????return?result; }); |
Desktop.vshost.exe Information: 0 : UI Thread : 9 Desktop.vshost.exe Information: 0 : Run calculation on thread: 10 Desktop.vshost.exe Information: 0 : Update UI on thread: 9
Invoke方法比較簡單,我們就不做過多的研究了,但是我們要考慮到一點(diǎn),Invoke是WinForm實(shí)現(xiàn)的UI跨線程溝通方式,WPF用的卻是Dispatcher,如果是在ASP.NET下跨線程之間的同步又怎么辦呢。為了兼容各種技術(shù)平臺(tái)下,跨線程同步的問題,Microsoft在.NET2.0的時(shí)候就引入了我們下面的這個(gè)對象。
SynchronizationContext上下文同步對象
為什么需要SynchronizationContext
就像我們在WinForm中遇到的問題一樣,有時(shí)候我們需要在一個(gè)線程中傳遞一些數(shù)據(jù)或者做一些操作到另一個(gè)線程。但是在絕大多數(shù)情況下這是不允許的,出于安全因素的考慮,每一個(gè)線程都有它獨(dú)立的內(nèi)存空間和上下文。因此在.NET2.0,微軟推出了SynchronizationContext。
它主要的功能之一是為我們提供了一種將一些工作任務(wù)(Delegate)以隊(duì)列的方式存儲(chǔ)在一個(gè)上下文對象中,然后把這些上下文對象關(guān)聯(lián)到具體的線程上,當(dāng)然有時(shí)候多個(gè)線程也可以關(guān)聯(lián)到同一個(gè)SynchronizationContext對象。獲取當(dāng)前線程的同步上下文對象可以使用SynchronizationContext.Current。同時(shí)它還為我們提供以下兩個(gè)方法Post和Send,分別是以異步和同步的方法將我們上面說的工作任務(wù)放到我們SynchronizationContext的隊(duì)列中。
SynchronizationContext示例
還是拿我們上面Invoke中用到的例子舉例,只是這次我們不直接調(diào)用控件的Invoke方法去更新它,而是寫了一個(gè)Report的方法專門去更新UI。
1 2 3 4 5 6 7 8 9 10 11 12 | double?sqrt = await Task<double>.Run(() => { ????Trace.TraceInformation("Current thread id is:{0}", Thread.CurrentThread.ManagedThreadId); ????double?result = 0; ????for?(int?i = 0; i < 50000000; i++) ????{ ????????result += Math.Sqrt(i); ????????Report(new?Tuple<int,?int>(50000000, i)); ????} ????return?result; }); |
每一次操作完之后我們調(diào)用一下Report方法,把我們總共要算的數(shù)字,以及當(dāng)前正在計(jì)算的數(shù)字傳給它就可以了。接下來就看我們的Report方法了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | private?SynchronizationContext m_SynchronizationContext; private?DateTime m_PreviousTime = DateTime.Now; public?Form1() { ????InitializeComponent(); ????// 在全局保存當(dāng)前UI線程的SynchronizationContext對象 ????m_SynchronizationContext = SynchronizationContext.Current; } public?void?Report(Tuple<int,?int> value) { ????DateTime now = DateTime.Now; ????if?((now - m_PreviousTime).Milliseconds > 100) ????{ ????????m_SynchronizationContext.Post((obj) => ????????{ ????????????Tuple<int,?int> minMax = (Tuple<int,?int>)obj; ????????????progressBar1.Maximum = minMax.Item1; ????????????progressBar1.Value = minMax.Item2; ????????}, value); ????????m_PreviousTime = now; ????} } |
整個(gè)操作看起來要比Inovke復(fù)雜一點(diǎn),與Invoke不同的是SynchronizationContext不需要對Control的引用,而Invoke必須先得有那個(gè)控件才能調(diào)用它的Invoke方法對它進(jìn)行操作。
總結(jié)
以上是生活随笔為你收集整理的winform什么时候会调用closed事件_async/await 给程序带来了什么?的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: micropython esp8266教
- 下一篇: 光纤接口怎么接 图解_光纤的数据比网线快