成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

詳解Winform多線程編程基本原理

開發 后端
本文將討論的是.NET并行編程中的Winform多線程編程基本原理,希望這些基礎能讓大家入門更加輕松。

本文在這里將從.NET并行計算講起,主要環境為Winform多線程編程。希望通過本文能對大家了解Winform多線程編程有所幫助,用好.NET并行計算。51CTO向您推薦《WinForm應用與開發視頻教程-WinForm教程

首先我們創建一個Winform的應用程序,在上面添加一個多行文本框和一個按鈕控件,按鈕的事件如下:

  1. Thread.Sleep(1000);  
  2. StringBuilder sb = new StringBuilder();  
  3. for (int i = 0; i < 10000; i++)  
  4.     sb.Append("test");  
  5. string s = sb.ToString();  
  6. textBox1.Text = s; 

首先我們可以把這個操作理解為一個非常耗時的操作,它至少占用1秒的時間。在1秒后,我們整了一個大字符串作為文本框的值,然后在標簽上顯示給文本框賦值這個UI渲染行為需要的時間,程序執行結果如下:

執行結果

我們可以感受到,在點擊了按鈕之后整個程序的UI就卡住了,沒有辦法拖動沒有辦法改變大小,用于體驗非常差。一般能想到會新建一個線程來包裝這個方法,使得UI線程不被卡住:

  1. new Thread(() =>  
  2. {  
  3.     Thread.Sleep(1000);  
  4.     StringBuilder sb = new StringBuilder();  
  5.     for (int i = 0; i < 10000; i++)  
  6.         sb.Append("test");  
  7.     string s = sb.ToString();  
  8.     textBox1.Text = s;  
  9. }).Start(); 

使用調試方式運行程序的話會得到如下的異常(非調試方式不會):

異常

雖然我們知道這樣設置:

  1. Control.CheckForIllegalCrossThreadCalls = false

可以屏蔽這個錯誤,但是在非創建控件的線程去更新控件的狀態的做法會導致很多問題,比如死鎖和控件部分被更新等。微軟推薦我們使用Control的Invoke或BeginInvoke方法來把涉及到控件狀態更新的操作讓UI線程去做:

  1. new Thread(() =>  
  2. {  
  3.     Invoke(new Action(() =>  
  4.     {  
  5.         Thread.Sleep(1000);  
  6.         StringBuilder sb = new StringBuilder();  
  7.         for (int i = 0; i < 10000; i++)  
  8.             sb.Append("test");  
  9.         string s = sb.ToString();  
  10.         textBox1.Text = s;  
  11.     }));  
  12. }).Start(); 

你可能會想到這么寫,但是運行程序后可以發現界面依然是卡死。想一下,雖然我們新開了一個線程,但是馬上又把整個代碼段交給UI線程去做了,當然起不到效果。其實這個方法的工作可以分為兩部分,一部分是我們數據的計算,一部分是把計算好的數據顯示在界面上,我們只應該把真正和UI相關的操作放到Invoke中讓UI線程去做: 

  1. new Thread(() =>  
  2. {  
  3.     Thread.Sleep(1000);  
  4.     StringBuilder sb = new StringBuilder();  
  5.     for (int i = 0; i < 10000; i++)  
  6.         sb.Append("test");  
  7.     string s = sb.ToString();  
  8.     Invoke(new Action(() =>  
  9.     {  
  10.         textBox1.Text = s;  
  11.     }));  
  12. }).Start(); 

再測試一次可以發現,UI在前1秒多的時間沒有卡死,在最后的一點時間還是卡死了。在繼續研究卡死問題之前我們來看一下,Control提供了InvokeRequired屬性來讓我們判斷當前線程是不是UI線程,或者說當前的操作是否需要進行Invoke:

  1. textBox1.Text = this.InvokeRequired.ToString();  
  2. new Thread(() =>  
  3. {  
  4.     textBox1.Text += Environment.NewLine + this.InvokeRequired.ToString();  
  5.     Invoke(new Action(() =>  
  6.     {  
  7.         textBox1.Text += Environment.NewLine + this.InvokeRequired.ToString();  
  8.     }));  
  9. }).Start();  

通過非調試方式啟動程序可以得到如下結果:

調試后的結果

很明顯:

1) 在線程外的賦值不需要Invoke(在UI線程)

2) 在線程內的賦值需要Invoke(不在UI線程)

3) 在Invoke中的賦值已經封送給UI線程,所以不需要Invoke

繼續研究卡死問題,您可能會想到,Control還提供了一個BeginInvoke方法,我們來試試看:

  1. new Thread(() =>  
  2. {  
  3.     Thread.Sleep(1000);  
  4.     StringBuilder sb = new StringBuilder();  
  5.     for (int i = 0; i < 10000; i++)  
  6.         sb.Append("test");  
  7.     string s = sb.ToString();  
  8.     BeginInvoke(new Action(() =>  
  9.     {  
  10.         textBox1.Text = s;  
  11.     }));  
  12. }).Start(); 

好像效果上還是沒什么區別,那么Invoke和BeginInvoke的區別在哪里呢?

我們知道Windows應用程序基于消息,Windows API提供了SendMessage和PostMessage兩個API,前者執行消息后返回(不經過消息管道,先于PostMessage執行),后者把消息發送到管道異步執行。Invoke和BeginInvoke從行為上來說類似這兩個API,但是實際上兩者都使用了PostMessage,前者使用信號量在消息執行前阻塞,達到同步的效果。我們來做一個實驗:

  1. new Thread(() =>  
  2. {  
  3.     Thread.Sleep(1000);  
  4.     StringBuilder sb = new StringBuilder();  
  5.     for (int i = 0; i < 10000; i++)  
  6.         sb.Append("test");  
  7.     string s = sb.ToString();  
  8.     Stopwatch sw = Stopwatch.StartNew();  
  9.     Invoke(new Action(() =>  
  10.     {  
  11.         textBox1.Text = s;  
  12.     }));  
  13.     MessageBox.Show(sw.ElapsedMilliseconds.ToString());  
  14. }).Start(); 

運行程序:

運行程序

可以體會到,在文本框的值出現之后才出現彈出框,文本框賦值這個消息的執行過程耗時2秒。把Invoke改為BeginInvoke其它不動再執行程序:

執行程序

明顯感到彈出框先顯示2秒后文本框的值出現。BeginInvoke沒有阻塞后續語句的執行。因此,需要注意,如果我們在方法中使用的變量在BeginInvoke之后有修改,極有可能發生混亂。如果您使用過委托的BeginInvoke應該會知道,通常建議總是調用EndInvoke來回收資源,對于Control的EndInvoke來說,如果您不需要獲取返回值的話,那么它不是必須的(來自msdn)。

#T#

現在您可能還有疑問為什么使用了BeginInvoke,UI還是卡了大概2秒,可以這么理解,我們把這么多的文字賦值到文本框中,這個UI行為是非常耗時的,不管是Invoke還是BeginInvoke最終是發送消息給UI線程處理(兩者都沒有使用線程池),它就是需要這么多時間,在一般情況下我們不會在UI上呈現這么多數據。

一般來說我們能做的優化是:

1) 盡量把非UI的操作使用新的線程去異步計算,不阻塞UI線程,真正需要操作UI的時候才去提交給UI線程

2) 盡量減少UI的操作復雜度,比如如果需要在UI上繪制一個復雜圖形可以在內存中先創建一個位圖,繪制好之后把整個位圖在UI上繪制,而不是直接在UI上繪制這個圖形

舉個例子,UI就好象一塊畫布,我們要在上面畫一個巨作怎么才能不過多占用這塊布的時間,讓大家都能用上呢?一個方法就是我們在準備顏色和畫筆的時候不占著這個布,真正要去畫的時候才去用,另外一個方法就是在另一塊畫布上先畫,然后把圖案采用復印的方式印到我們的主畫布上。

對于大量數據的呈現,我們還可以:

1) 采用分頁,只顯示一部分數據,對于Windows程序的分頁可能就是滾動條性質的了,在滾動條下拉的時候再去呈現當前“頁”的數據

2) 即使是一頁的數據,也可以一部分一部分呈現

舉個例子,對于word文檔的加載一般我們一打開就可以看到第一頁,然后滾動塊慢慢變小,頁數慢慢增多,如果一開始就加載1000頁的話我們可能要1分鐘后才能看到第一頁,如果等不及直接向后翻滾動條的話會立即加載后面的數據:

  1. new Thread(() =>  
  2. {  
  3.     StringBuilder sb = new StringBuilder();  
  4.     for (int i = 0; i < 100; i++)  
  5.         sb.Append("test");  
  6.     string s = sb.ToString();  
  7.     for (int i = 0; i < 20; i++)  
  8.     {  
  9.         BeginInvoke(new Action(() =>  
  10.         {  
  11.             textBox1.Text += s + i;  
  12.         }));  
  13.         Thread.Sleep(10);  
  14.     }  
  15. }).Start(); 

設置文本框允許縱向滾動條并且運行一下這段程序可以體會到這個效果:

最終效果

原文標題:淺談.NET下的多線程和并行計算(八)Winform多線程編程基礎上

鏈接:http://www.cnblogs.com/lovecindywang/archive/2010/01/06/1640267.html

責任編輯:彭凡 來源: 博客園
相關推薦

2012-01-12 14:37:34

jQuery

2019-04-30 08:15:31

2011-11-29 12:17:00

2009-02-24 09:43:00

IP電話原理

2016-08-17 23:53:29

網絡爬蟲抓取系統

2021-02-08 21:40:04

SockmapBPF存儲

2019-11-28 10:45:28

ZooKeeper源碼分布式

2016-08-18 00:04:09

網絡爬蟲抓取系統服務器

2010-08-20 13:29:33

OFDM

2013-04-07 14:09:55

Android應用基本

2020-03-21 14:57:14

手機定位智能手機APP

2009-12-29 10:19:32

ADSL寬帶接入網

2010-03-17 13:35:02

2011-07-07 14:46:10

Cocoa Xcode

2010-03-18 20:13:03

Java socket

2011-07-07 14:10:21

Cocoa 內省 hash

2009-06-11 09:56:09

MySQL Repli原理

2020-12-29 16:55:44

ZooKeeper運維數據結構

2012-09-28 10:12:55

2024-01-30 09:21:01

deltaFifo機制apiServer
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 羞羞视频在线免费 | 国产伦一区二区三区 | 国产精品成人免费 | 91视频网址| 一区二区三区四区在线播放 | 精品欧美乱码久久久久久 | 色成人免费网站 | 国产免费一区二区三区 | 亚洲欧美日本国产 | 日韩三级 | 另类专区亚洲 | 精品欧美一区免费观看α√ | 欧美日韩三级视频 | 一区二区高清在线观看 | 免费观看一级毛片 | 日韩精品一区二区三区在线观看 | 日韩精品一区二区三区中文字幕 | 毛片视频免费 | 日韩欧美成人精品 | 久久大陆 | 国产福利91精品一区二区三区 | 成人欧美一区二区三区黑人孕妇 | 日韩精品一区二区三区中文在线 | 日韩一级免费大片 | 久久国产亚洲精品 | 久久er99热精品一区二区 | 国产激情视频网站 | 日韩在线一区二区 | 欧美午夜精品久久久久久浪潮 | 韩国欧洲一级毛片 | 成人精品久久 | 日韩一区在线播放 | 亚州精品天堂中文字幕 | 亚洲狠狠爱 | 国产精品久久久久久久久免费相片 | 国产欧美日韩一区二区三区在线观看 | 国产一区二 | 伊人网在线播放 | 午夜精品久久久 | 日本人麻豆| 亚洲一区在线播放 |