winform什么时候会调用closed事件_async/await 给程序带来了什么?
如果說async給ASP.NET帶來的是處理能力的提高,那么在WinForm中給程序員帶來的好處則是最大的。我們再也不用因為要實現異步寫回調或者綁定事件了,省事了,可讀性也提高了。不信你看下面我們將調用我們那個web service的代碼在.NET4.5下實現一下:
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線程就可以回去響應UI了,在上面的代碼中我們是沒有新線程產生的,和EAP一樣拿到結果直接就可以對UI操作了。
async/await似乎真的很好,但是如果我們await后面的代碼執行在另外一個線程中會發生什么事情呢?
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; } |
我們在界面中放了一個ProgressBar,同時開一個線程去把從1到5000000的平方全部加起來,看起來是一個非常耗時的操作,于是我們用Task.Run開了一個新的線程去執行。(注:如果是純運算的操作,多線程操作對性能沒有多大幫助,我們這里主要是想給UI一個進度顯示當前進行到哪一步了。)看起來沒有什么問題,我們按F5運行吧!
Bomb~
當執行到這里的時候,程序就崩潰了,告訴我們”無效操作,只能從創建porgressBar的線程訪問它。“ ?這也是我們一開始提到的,在WinForm程序中,只有UI主線程才能對UI進行操作,其它的線程是沒有權限的。接下來我們就來看看,如果在WinForm中實現非UI線程對UI控制的更新操作。?
萬能的Invoke
WinForm中絕大多數的控件包括窗體在內都實現了Invoke方法,可以傳入一個Delegate,這個Delegate將會被擁有那個控制的線程所調用,從而避免了跨線程訪問的問題。
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方法比較簡單,我們就不做過多的研究了,但是我們要考慮到一點,Invoke是WinForm實現的UI跨線程溝通方式,WPF用的卻是Dispatcher,如果是在ASP.NET下跨線程之間的同步又怎么辦呢。為了兼容各種技術平臺下,跨線程同步的問題,Microsoft在.NET2.0的時候就引入了我們下面的這個對象。
SynchronizationContext上下文同步對象
為什么需要SynchronizationContext
就像我們在WinForm中遇到的問題一樣,有時候我們需要在一個線程中傳遞一些數據或者做一些操作到另一個線程。但是在絕大多數情況下這是不允許的,出于安全因素的考慮,每一個線程都有它獨立的內存空間和上下文。因此在.NET2.0,微軟推出了SynchronizationContext。
它主要的功能之一是為我們提供了一種將一些工作任務(Delegate)以隊列的方式存儲在一個上下文對象中,然后把這些上下文對象關聯到具體的線程上,當然有時候多個線程也可以關聯到同一個SynchronizationContext對象。獲取當前線程的同步上下文對象可以使用SynchronizationContext.Current。同時它還為我們提供以下兩個方法Post和Send,分別是以異步和同步的方法將我們上面說的工作任務放到我們SynchronizationContext的隊列中。
SynchronizationContext示例
還是拿我們上面Invoke中用到的例子舉例,只是這次我們不直接調用控件的Invoke方法去更新它,而是寫了一個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; }); |
每一次操作完之后我們調用一下Report方法,把我們總共要算的數字,以及當前正在計算的數字傳給它就可以了。接下來就看我們的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(); ????// 在全局保存當前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; ????} } |
整個操作看起來要比Inovke復雜一點,與Invoke不同的是SynchronizationContext不需要對Control的引用,而Invoke必須先得有那個控件才能調用它的Invoke方法對它進行操作。
總結
以上是生活随笔為你收集整理的winform什么时候会调用closed事件_async/await 给程序带来了什么?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: micropython esp8266教
- 下一篇: python编程口诀_科学网—Pytho