C#中跨線程操作控件 --- InvokeRequired 屬性 與Invoke方法

在設計中爲了讓界面與邏輯分離,我的做法是使用事件,界面只要響應事件來處理界面的顯示就行了。而事件在邏輯處理中可能由不同的線程引發,這些事件的響應方法在修改界面中的控件內容時便會引發一個異常。

這時就用到了Control.InvokeRequired 屬性 與Invoke方法。

MSDN中說:
獲取一個值,該值指示調用方在對控件進行方法調用時是否必須調用 Invoke 方法,因爲調用方位於創建控件所在的線程以外的線程中。 
如果控件的 Handle 是在與調用線程不同的線程上創建的(說明您必須通過 Invoke 方法對控件進行調用),則爲 true;否則爲 false。
Windows 窗體中的控件被綁定到特定的線程,不具備線程安全性 。因此,如果從另一個線程調用控件的方法,那麼必須使用控件的一個 Invoke 方法來將調用封送到適當的線程。該屬性可用於確定是否必須調用 Invoke 方法,當不知道什麼線程擁有控件時這很有用。

下面來說下這個的用法(我的一般做法):
首先定義一個委託,與這個事件處理函數的簽名一樣委託,當然直接使用該事件的委託也是可以的,如:

 

private   delegate   void InvokeCallback( string msg); 


然後就是判斷這個屬性的值來決定是否要調用Invoke函數:

 

void m_comm_MessageEvent( string msg)
      {
     if (txtMessage.InvokeRequired)
       {
     InvokeCallbackmsgCallback =   new InvokeCallback(m_comm_MessageEvent);
      txtMessage.Invoke(msgCallback, new   object []   { msg } );
    } 
     else 
       {
     txtMessage.Text = msg;
    } 
   } 


說明:這個函數就是事件處理函數,txtMessage是一個文本框。
這樣就做到了窗體中控件的線程安全性。

------------------

InvokeRequired 當前線程不是創建控件的線程時爲true
比如你可以自己開一個Thread,或使用Timer的事件來訪問窗體上的控件的時候,在線程中窗體的這個屬性就是True的。

簡單的說,如果有兩個線程,Thread A和Thread B,並且有一個Control c,是在Thread A裏面new的。
那麼在Thread A裏面運行的任何方法調用c.InvokeRequired都會返回false。
相反,如果在Thread B裏面運行的任何方法調用c.InvokeRequired都會返回true。
是否是UI線程與結果無關。(通常Control所在的線程是UI線程,但是可以有例外)

也可以認爲,在new Control()的時候,control用一個變量記錄下了當前線程,在調用InvokeRequired時,返回當前線程是否不等於new的時候記錄下來的那個線程。

我理解:如果InvokeRequired==true表示其它線程需要訪問控件,那麼調用invoke來轉給控件owner處理。

下面是示例源碼

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 WinThreadNew
{
    
public partial class WinThreadNew : Form
    
{
        
int pro = 0;
        Thread backWork 
= null;
        
public WinThreadNew()
        
{
            InitializeComponent();
        }


        
private void btnStart_Click(object sender, EventArgs e)
        
{
            backWork 
= new Thread(new ThreadStart(doWork));
            backWork.Start();
        }


        
public delegate void DelegateUpdateUIPro();
        
private void doWork()
        
{
            
for (int i = 0; i < 100; i++ )
            
{
                pro
++;
                UpdateUIPro();
                Thread.Sleep(
100);
            }

            
        }


        
private void UpdateUIPro()
        
{
            
if (this.InvokeRequired)
            
{
                
this.BeginInvoke(new DelegateUpdateUIPro(UpdateUIPro));
            }
 
            
else
            
{
                
this.lblPro.Text = pro.ToString() + "%";
            }

        }


        
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        
{
            
this.backWork.Abort();
            
this.backWork.Join();
        }

    }

}

發佈了17 篇原創文章 · 獲贊 9 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章