亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

在多線程中調用winform窗體控件的實現方法

2020-01-24 02:29:36
字體:
來源:轉載
供稿:網友

本文實例講述了在C#中實現多線程中調用winform窗體控件的方法,對于C#程序設計的學習有著很好的借鑒參考價值。具體方法如下:

首先,由于Windows窗體控件本質上不是線程安全的。因此如果有兩個或多個線程適度操作某一控件的狀態(set value),則可能會迫使該控件進入一種不一致的狀態。還可能出現其他與線程相關的 bug,包括爭用和死鎖的情況。于是在調試器中運行應用程序時,如果創建某控件的線程之外的其他線程試圖調用該控件,則調試器會引發一個 InvalidOperationException

本文用一個很簡單的示例來講解這個問題(在窗體上放一個TextBox和一個Button,點擊Button后,在新建的線程中設置TextBox的值)

解決辦法一: 關閉該異常檢測的方式來避免異常的出現

經過測試發現此種方法雖然避免了異常的拋出,但是并不能保證程序運行結果的正確性 (比如多個線程同時設置TextBox1的Text時,很難預計最終TextBox1的Text是什么)

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Threading;namespace winformTest{  public partial class Form1 : Form  {    public Form1()    {      InitializeComponent();      Control.CheckForIllegalCrossThreadCalls = false;//這一行是關鍵       }        private void button1_Click(object sender, EventArgs e)    {      SetTextBoxValue();    }    void SetTextBoxValue()    {      TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method1");      ThreadStart TS = new ThreadStart(tbsv.SetText);      Thread T = new Thread(TS);      T.Start();    }    class TextBoxSetValue    {      private TextBox _TextBox ;      private string _Value;      public TextBoxSetValue(TextBox TxtBox, String Value)       {        _TextBox = TxtBox;        _Value = Value;      }      public void SetText()       {        _TextBox.Text = _Value;      }    }  }}

解決辦法二:通過委托安全調用

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;namespace winformTest{  public partial class Form2 : Form  {    public Form2()    {      InitializeComponent();    }    private void button1_Click(object sender, EventArgs e)    {      SetTextBoxValue();    }        private delegate void CallSetTextValue();    //通過委托調用    void SetTextBoxValue()     {      TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method2");      if (tbsv.TextBox.InvokeRequired)      {        CallSetTextValue call = new CallSetTextValue(tbsv.SetText);        tbsv.TextBox.Invoke(call);              }      else      {        tbsv.SetText();      }    }    class TextBoxSetValue    {      private TextBox _TextBox;      private string _Value;      public TextBoxSetValue(TextBox TxtBox, String Value)      {        _TextBox = TxtBox;        _Value = Value;      }      public void SetText()      {        _TextBox.Text = _Value;      }      public TextBox TextBox {        set { _TextBox = value; }        get { return _TextBox; }      }          }  }}

第三解決辦法:利用BackgroundWorker控件

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;using System.Threading;namespace winformTest{  public partial class Form3 : Form  {    public Form3()    {      InitializeComponent();    }    private void button1_Click(object sender, EventArgs e)    {       using (BackgroundWorker bw = new BackgroundWorker())      {        bw.RunWorkerCompleted += SetTextBoxValue;        bw.RunWorkerAsync();      }    }     void SetTextBoxValue(object sender, RunWorkerCompletedEventArgs e)     {      TextBoxSetValue tbsv = new TextBoxSetValue(this.textBox1, "Method3");      tbsv.SetText();    }    class TextBoxSetValue    {      private TextBox _TextBox;      private string _Value;      public TextBoxSetValue(TextBox TxtBox, String Value)      {        _TextBox = TxtBox;        _Value = Value;      }      public void SetText()      {        _TextBox.Text = _Value;      }    }  }}

用戶不喜歡反應慢的程序。在執行耗時較長的操作時,使用多線程是明智之舉,它可以提高程序 UI 的響應速度,使得一切運行顯得更為快速。在 Windows 中進行多線程編程曾經是 C++ 開發人員的專屬特權,但是現在,可以使用所有兼容 Microsoft .NET 的語言來編寫。

不過Windows 窗體體系結構對線程使用制定了嚴格的規則。如果只是編寫單線程應用程序,則沒必要知道這些規則,這是因為單線程的代碼不可能違反這些規則。然而,一旦采用多線程,就需要理解 Windows 窗體中最重要的一條線程規則:除了極少數的例外情況,否則都不要在它的創建線程以外的線程中使用控件的任何成員。本規則的例外情況有文檔說明,但這樣的情況非常少。這適用于其類派生自 System.Windows.Forms.Control 的任何對象,其中幾乎包括 UI 中的所有元素。所有的 UI 元素(包括表單本身)都是從 Control 類派生的對象。此外,這條規則的結果是一個被包含的控件(如,包含在一個表單中的按鈕)必須與包含它控件位處于同一個線程中。也就是說,一個窗口中的所有控件屬于同一個 UI 線程。實際中,大部分 Windows 窗體應用程序最終都只有一個線程,所有 UI 活動都發生在這個線程上。這個線程通常稱為 UI 線程。這意味著您不能調用用戶界面中任意控件上的任何方法,除非在該方法的文檔說明中指出可以調用。該規則的例外情況(總有文檔記錄)非常少而且它們之間關系也不大。請注意,以下代碼是非法的:

private Thread myThread;private void Form1_Load(object sender, EventArgs e){  myThread = new Thread(new ThreadStart(RunsOnWorkerThread));  myThread.Start();}private void RunsOnWorkerThread(){  label1.Text = "myThread線程調用UI控件";}

如果您在 .NET Framework 1.0 版本中嘗試運行這段代碼,也許會僥幸運行成功,或者初看起來是如此。這就是多線程錯誤中的主要問題,即它們并不會立即顯現出來。甚至當出現了一些錯誤時,在第一次演示程序之前一切看起來也都很正常。但不要搞錯 ― 我剛才顯示的這段代碼明顯違反了規則,并且可以預見,任何抱希望于“試運行時良好,應該就沒有問題”的人在即將到來的調試期是會付出沉重代價的。

下面我們來看看有哪些方法可以解決這一問題。

一、System.Windows.Forms.MethodInvoker 類型是一個系統定義的委托,用于調用不帶參數的方法。

private Thread myThread;private void Form1_Load(object sender, EventArgs e){  myThread = new Thread(new ThreadStart(RunsOnWorkerThread));  myThread.Start();}private void RunsOnWorkerThread(){  MethodInvoker mi = new MethodInvoker(SetControlsProp);  BeginInvoke(mi);}private void SetControlsProp(){  label1.Text = "myThread線程調用UI控件";}

二、直接用System.EventHandle(可帶參數)

private Thread myThread;private void Form1_Load(object sender, EventArgs e){  myThread = new Thread(new ThreadStart(RunsOnWorkerThread));  myThread.Start();}private void RunsOnWorkerThread(){  //DoSomethingSlow();  string pList = "myThread線程調用UI控件";  label1.BeginInvoke(new System.EventHandler(UpdateUI), pList);}//直接用System.EventHandler,沒有必要自定義委托private void UpdateUI(object o, System.EventArgs e){  //UI線程設置label1屬性  label1.Text = o.ToString() + "成功!";}

三、包裝 Control.Invoke

雖然第二個方法中的代碼解決了這個問題,但它相當繁瑣。如果輔助線程希望在結束時提供更多的反饋信息,而不是簡單地給出“Finished!”消息,則 BeginInvoke過于復雜的使用方法會令人生畏。為了傳達其他消息,例如“正在處理”、“一切順利”等等,需要設法向 UpdateUI 函數傳遞一個參數??赡苓€需要添加一個進度欄以提高反饋能力。這么多次調用 BeginInvoke 可能導致輔助線程受該代碼支配。這樣不僅會造成不便,而且考慮到輔助線程與 UI 的協調性,這樣設計也不好。對這些進行分析之后,我們認為包裝函數可以解決這兩個問題。

private Thread myThread;private void Form1_Load(object sender, EventArgs e){  myThread = new Thread(new ThreadStart(RunsOnWorkerThread));  myThread.Start();}private void RunsOnWorkerThread(){  ////DoSomethingSlow();  for (int i = 0; i < 100; i++)  { ShowProgress( Convert.ToString(i)+"%", i); Thread.Sleep(100);  }}public void ShowProgress(string msg, int percentDone){  // Wrap the parameters in some EventArgs-derived custom class:  System.EventArgs e = new MyProgressEvents(msg, percentDone);  object[] pList = { this, e };  BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);}private delegate void MyProgressEventsHandler(object sender, MyProgressEvents e);private void UpdateUI(object sender, MyProgressEvents e){  lblStatus.Text = e.Msg;  myProgressControl.Value = e.PercentDone;}public class MyProgressEvents : EventArgs{  public string Msg;  public int PercentDone;  public MyProgressEvents(string msg, int per)  {    Msg = msg;    PercentDone = per;  }}

ShowProgress 方法對將調用引向正確線程的工作進行封裝。這意味著輔助線程代碼不再擔心需要過多關注 UI 細節,而只要定期調用 ShowProgress 即可。

如果我提供一個設計為可從任何線程調用的公共方法,則完全有可能某人會從 UI 線程調用這個方法。在這種情況下,沒必要調用 BeginInvoke,因為我已經處于正確的線程中。調用 Invoke 完全是浪費時間和資源,不如直接調用適當的方法。為了避免這種情況,Control 類將公開一個稱為 InvokeRequired 的屬性。這是“只限 UI 線程”規則的另一個例外。它可從任何線程讀取,如果調用線程是 UI 線程,則返回假,其他線程則返回真。這意味著我可以按以下方式修改包裝:

public void ShowProgress(string msg, int percentDone){  if (InvokeRequired)  { // As before //...  }  else  { // We're already on the UI thread just // call straight through. UpdateUI(this, new MyProgressEvents(msg,PercentDone));  }}

線程打開窗體的問題:

using System;using System.Collections.Generic;using System.ComponentModel;using System.Data;using System.Drawing;using System.Text;using System.Windows.Forms;namespace WindowsApplication36{  public partial class Form1 : Form  {    public Form1()    {      InitializeComponent();    }    private void Form1_Load(object sender, EventArgs e)    {    }    private void button1_Click(object sender, EventArgs e)    {      new System.Threading.Thread(new System.Threading.ThreadStart(invokeShow)).Start();    }    public void invokeShow()    {       Form f1 = new Form();       this.Invoke(new System.EventHandler(this.showForm), new object[] { f1, null });    }    public void showForm(object sender,EventArgs e)    {      Form f1 = sender as Form;      f1.ShowDialog();    }  }}
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩精品免费在线播放| 国产精品白嫩初高中害羞小美女| 国产一区二区激情| 欧美黑人性生活视频| 亚洲天堂第一页| 精品丝袜一区二区三区| 亚洲精品99999| 欧美日韩综合视频网址| 91高潮精品免费porn| 日韩av在线免费观看一区| 日韩免费精品视频| 亚洲精品资源美女情侣酒店| 中文字幕国产精品久久| 国产精品观看在线亚洲人成网| 成人免费激情视频| 欧美亚洲国产成人精品| 日韩欧美一区二区三区| 成人做爰www免费看视频网站| wwwwwwww亚洲| 日韩一区二区久久久| 欧美孕妇与黑人孕交| 国产精品a久久久久久| 亚洲精品资源在线| 在线观看日韩欧美| 精品偷拍一区二区三区在线看| 欧美日韩国产一区中文午夜| 色噜噜狠狠色综合网图区| 中文在线资源观看视频网站免费不卡| 这里只有精品在线播放| 亚洲欧美在线免费| 久久综合国产精品台湾中文娱乐网| 亚洲欧洲第一视频| 中国人与牲禽动交精品| 亚洲影影院av| 国产99久久精品一区二区| 亚洲精品按摩视频| 欧美人与性动交a欧美精品| 一区二区亚洲欧洲国产日韩| 欧美亚洲一区在线| 亚洲自拍欧美色图| 91在线视频成人| 91精品国产高清| 久久精品青青大伊人av| 精品国产福利视频| 久久综合亚洲社区| 国产成人精品一区二区| 亚洲人成网7777777国产| 菠萝蜜影院一区二区免费| 精品高清一区二区三区| 欧美激情区在线播放| 91禁外国网站| 欧美xxxx14xxxxx性爽| 亚洲伊人一本大道中文字幕| 一区二区三欧美| 91精品国产高清久久久久久| 中文欧美日本在线资源| 97视频在线播放| 日韩精品视频免费专区在线播放| 中文字幕精品www乱入免费视频| 欧美精品videos另类日本| 亚洲激情电影中文字幕| 色爱精品视频一区| 亚洲精品动漫100p| 一本一本久久a久久精品牛牛影视| 欧美精品中文字幕一区| 亚洲性日韩精品一区二区| 亚洲a∨日韩av高清在线观看| 91精品国产色综合| 国产精品久久视频| 亚洲人成网站999久久久综合| 怡红院精品视频| 欧美激情精品久久久| 亚洲第一男人天堂| 久久久久久久久久久久av| 国产日韩欧美夫妻视频在线观看| 国产精品影片在线观看| 欧美在线播放视频| 国产午夜精品免费一区二区三区| 中文字幕视频在线免费欧美日韩综合在线看| 91成人福利在线| 国产精品久久久久不卡| 亚洲精品www久久久久久广东| 欧美精品在线网站| 国产精自产拍久久久久久| 亚洲国产福利在线| zzijzzij亚洲日本成熟少妇| 亚洲一区二区久久久| 国产精品18久久久久久麻辣| 在线色欧美三级视频| 国产精品极品在线| 日韩av电影院| 国产精品大陆在线观看| 国产伦精品免费视频| 麻豆国产精品va在线观看不卡| 久久免费高清视频| 精品呦交小u女在线| 亚洲欧洲第一视频| 91干在线观看| 亚洲综合日韩中文字幕v在线| 影音先锋欧美精品| 欧美激情一区二区三区久久久| 久久视频免费在线播放| 亚洲欧美日韩成人| 色吧影院999| 成人午夜高潮视频| 亚洲毛片一区二区| 日韩高清免费观看| 国产美女久久精品| 国产欧美一区二区三区四区| 国产欧美日韩精品专区| 欧美xxxx14xxxxx性爽| 97精品国产97久久久久久免费| 精品视频中文字幕| 91在线精品播放| 国产精品视频免费在线| 欧美日韩免费在线观看| 九色91av视频| 亚洲国产成人精品久久久国产成人一区| 在线色欧美三级视频| 伦理中文字幕亚洲| 中文字幕在线日韩| 国产精品久久久久久久久久久久久| 欧美在线激情视频| 日韩精品高清在线观看| 国产综合在线看| 国产丝袜精品视频| 亚洲天堂精品在线| 精品日韩美女的视频高清| 欧美三级免费观看| 欧美精品国产精品日韩精品| 中文字幕久热精品视频在线| 5278欧美一区二区三区| 精品国产福利视频| 最近2019好看的中文字幕免费| 午夜精品久久久久久久99热| 精品久久久久久久久久| 欧美精品www| 欧美午夜性色大片在线观看| 精品国产91久久久久久老师| 久久久国产一区二区三区| 亚洲精品综合久久中文字幕| 欧美国产日韩视频| 欧美精品生活片| 欧美激情手机在线视频| 国产精品视频最多的网站| 欧日韩不卡在线视频| 国内精品视频久久| 国产不卡一区二区在线播放| 日韩中文字幕欧美| 国产精品久久久久福利| 国产欧美欧洲在线观看| 日韩av电影在线网| 成人中文字幕+乱码+中文字幕| 国产日韩欧美另类| 91久久精品在线| 亚洲新声在线观看| 亚洲精品国偷自产在线99热| 日韩电影免费在线观看| 久久久久久中文| 日韩av电影手机在线观看| 亚洲综合国产精品| 欧美性在线视频| 在线亚洲午夜片av大片| 欧美性受xxxx白人性爽|