C#跨線程操作控件的最簡單實現探究


隨着程序復雜度的提高,程序不可避免會出現多個線程,此時就很可能存在跨線程操作控件的問題。

 

跨線程操作UI控件主要有三類方式:

1、禁止系統的線程間操作檢查。(此法不建議使用

2、使用Invoke(同步)或者BeginInvoke(異步)。(使用委托實現,並用lambda表達式簡化代碼

3、使用BackgroundWorker組件。(此法暫不介紹,詳情可見文末的參考資料

 

先看一個跨線程操作失敗的例子:

新建一個Winform窗口程序項目,拖一個button1和label1控件到Form1窗體上。啟動程序以后試圖通過點擊button1改變label1的值,完整代碼如下:

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨線程調用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12         }
13 
14         private void button1_Click(object sender, EventArgs e)
15         {
16             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
17             thread1.Start("label已更新");
18         }
19         
20         private void UpdateLabel(object str)
21         {
22             label1.Text = str.ToString();
23         }
24     }
25 }

點擊button1以后運行報錯:

 

解決方案:

方法一:禁止系統的線程間操作檢查。

代碼就一句話:Control.CheckForIllegalCrossThreadCalls = false;通常寫在Form1類的構造方法Form1()中。如下所示:

1 public Form1()
2         {
3             InitializeComponent();
4 
5             Control.CheckForIllegalCrossThreadCalls = false;  
6         }

但是,這種方法是很不可靠的,有時候還是會報錯。

 

方法二:使用Invoke(同步)或者BeginInvoke(異步)最精簡的代碼如下:

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨線程調用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12         }
13 
14         private void button1_Click(object sender, EventArgs e)
15         {
16             Thread thread1 = new Thread(UpdateLabel);//可以省略線程的委托類型ParameterizedThreadStart
17             thread1.Start("label已更新");
18         }
19 
20         private void UpdateLabel(object str)
21         {
22             if (label1.InvokeRequired)//不同線程為true,所以這里是true
23             {               
24                 BeginInvoke(new Action<string> (x => {label1.Text = x.ToString();}),str);  
25             }
26         }
27     }
28 }

說明:Action是.NET預定義好的委托,可以簡化委托的語法,如果不清楚它的用法,可以搜索“Action和Func的用法”。

 

將上面的兩種方法總結在同一段程序里面如下所示:(注意看代碼中的注釋)

 1 using System;
 2 using System.Threading;
 3 using System.Windows.Forms;
 4 
 5 namespace Windows跨線程調用控件
 6 {
 7     public partial class Form1 : Form
 8     {
 9         public Form1()
10         {
11             InitializeComponent();
12 
13             /************* 方法一 ************/
14             //Control.CheckForIllegalCrossThreadCalls = false;
15 
16         }
17 
18         private void button1_Click(object sender, EventArgs e)
19         {
20             Thread thread1 = new Thread(new ParameterizedThreadStart(UpdateLabel));
21             thread1.Start("label已更新");
22         }
23 
24 
25         //如果控件的 Handle(句柄) 是在與調用線程不同的線程上創建的(說明您必須通過 Invoke 方法對控件進行調用),則為 true;否則為 false。
26         private void UpdateLabel(object str)
27         {
28             if (label1.InvokeRequired)//當是不同的線程在訪問時為true,所以這里是true
29             {
30                 /************* 方法二 ************/
31                 //Action<string> actionDelegate = (x) => { this.label1.Text = x.ToString(); };
32 
33                 ////如果已經創建控件的句柄,則除了 InvokeRequired 屬性以外,控件上還有四個可以從【任何線程】上安全調用的方法,
34                 ////它們是:Invoke、BeginInvoke、EndInvoke 和 CreateGraphics。 
35                 ////在后台線程上創建控件的句柄之前調用 CreateGraphics 可能會導致非法的跨線程調用。 
36                 ////對於所有其他方法調用,則應使用調用 (invoke) 方法之一封送對控件的線程的調用。
37                 //this.label1.BeginInvoke(actionDelegate, str);
38 
39 
40                 /************* 方法二(變式) ************/
41                 //也可以直接用下面一句話來完成
42                 //Control.BeginInvoke 方法有兩個重載:BeginInvoke(Delegate)    ,BeginInvoke(Delegate, Object[]),下式用的是第二個重載
43                 this.BeginInvoke(new Action<string>((x) => { label1.Text = x.ToString(); }), str); 44 
45                 //如果啟動的多線程不需要帶可變的參數,那更簡單:
46                 //label1.BeginInvoke(new Action(() => { label1.Text = "aaa"; }));         
47             }
48         }
49     }
50 }

參考資料:

http://www.cnblogs.com/TankXiao/p/3348292.html

http://www.cnblogs.com/txw1958/archive/2012/08/21/csharp-crossthread-widget.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM