Control.Invoke和Control.BeginInvoke簡述

來源:互聯網
上載者:User

1.背景介紹
        在Windows Form中使用多線程時,除了建立控制項的線程以外(就是主線程),絕對不要在任何其他線程裡面調用控制項的成員(只有極個別情況例外),也就是說控制項屬於建立它的線程,不能從其他線程裡面訪問。這一條適用於所有從System.Windows.Forms.Control派生的控制項(因此可以說是幾乎所有控制項),包括Form控制項本身也是。舉一反三,我們很容易得出這樣的結論,控制項的子控制項必須由建立控制項的線程來建立,比如一個表單上的按鈕,比如由建立表單的線程來建立,因此,一個視窗中的所有控制項實際上都活在同一個線程之中。在實際編程時,大多數的軟體的做法都是讓同一線程負責全部的控制項,這就是我們所說的UI線程(就是主線程)。

      由於上述限制,我們可能會感到很不方便,的確,當我們利用一個新建立的線程來執行某些花時間的運算時,怎樣知道運算進度如何並通過UI反映給使用者呢?解決方案很多!比如熟悉多線程編程的使用者很快會想到,我們採用一些低級的同步方法,工作者線程把狀態儲存到一個同步對象中,讓UI線程輪詢(Polling)該對象並反饋給使用者就可以了。不過,這還是挺麻煩的,實際上不用這樣做,Control類(及其衍生類別)對象有一個Invoke方法很特別,這是少數幾個不受線程限制的成員之一。我們前面說到,絕對不要在任何其他線程裡面調用非本線程建立的控制項的成員時,也說了“只有極個別情況例外”,這個Invoke方法就是極個別情況之一----Invoke方法可以從任何線程裡面調用。

2.Invoke,BeginInvoke介紹
      Invoke方法的參數很簡單,一個委託,一個參數表(可選),而Invoke方法的主要功能就是協助你在UI線程(即建立控制項的線程)上調用委託所指定的方法。Invoke方法首先檢查發出調用的線程(即當前線程)是不是UI線程,如果是,直接執行委託指向的方法,如果不是,它將切換到UI線程,然後執行委託指向的方法。不管當前線程是不是UI線程,Invoke都阻塞直到委託指向的方法執行完畢,然後切換回傳出調用的線程(如果需要的話),返回。注意,使用Invoke方法時,UI線程不能處於阻塞狀態。以下MSDN裡關於Invoke方法的說明:
   “控制項上有四種方法可以安全地從任何線程進行調用:Invoke、BeginInvoke、EndInvoke 和CreateGraphics。對於所有其他方法調用,則應使用調用 (invoke) 方法之一封送對控制項的線程的調用。
  委託可以是 EventHandler 的執行個體,在此情況下,發送方參數將包含此控制項,而事件參數將包含 EventArgs.Empty。委託還可以是 MethodInvoker 的執行個體或採用 void 參數列表的其他任何委託。調用 EventHandler 或 MethodInvoker 委託比調用其他類型的委託速度更快。”
     說說BeginInvoke,毫無疑問這是Invoke的非同步版本(Invoke是同步完成的),BeginInvoke方法總是使用UI線程。相對Invoke而言,使用BeginInvoke稍稍麻煩一點,但還是那句話,非同步比同步效果好,儘管複雜些。比如同步方法可能出現這樣一種死結情況:工作者線程通過Invoke同步調用UI線程裡的方法時會阻塞,而萬一UI線程正在等待工作者線程做某件事時怎麼辦?因此,能夠使用非同步方法呼叫時應盡量使用非同步方法呼叫。
下面是一個具體的例子來說明Invoke和BeginInvoke的區別:

namespace ControlInvoke_BeginInvoke
{
    public partial class Form1 : Form
    {
        private Thread myThread;
        private delegate void DisplayMethod(string content);
        private delegate void BeginInvokeDelegate();
        string strContent = string.Empty;
        public Form1()
        {
            InitializeComponent();
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            if (myThread != null && myThread.IsAlive)
            {
                this.btnStart.Text = "Stop";
                myThread.Abort();
            }
            else
            {
                this.txtMain.Text = string.Empty;
                string strContent = string.Empty;
                this.btnStart.Text = "Start";
                this.myThread = new Thread(new ThreadStart(GetContent));
                this.myThread.Start();
            }
        }

        private void GetContent()
        {
                this.Invoke(new DisplayMethod(Refresh_DisplayContent), "Hello World");
                MessageBox.Show("BeginInvoke");
        }
        private void Refresh_DisplayContent(string content)
        {
            Thread.Sleep(5000);
            lock (this.strContent)
            {
                this.strContent += content + "\r\n";
            }
            this.txtMain.Text = this.strContent;
            this.txtMain.Refresh();
        }
    }
}

上面代碼執行結果將是畫面上先顯示"Hello World“,然後再彈出對話方塊。如果使用this.BeginInvoke
        private void GetContent()
        {
                this.BeginInvoke(new DisplayMethod(Refresh_DisplayContent), "Hello World");
                MessageBox.Show("BeginInvoke");
        }
結果正好相反,由於BeginInvoke為非同步作業,即Refresh_DisplayContent方法的執行相對於myThread 線程是非同步。

本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/chenlu1982/archive/2008/02/18/2102692.aspx

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.