實戰.Net多執行緒(二)

草上爬發表於2008-08-10
在上一篇博文中,我們已經實現了簡單的非同步方法呼叫,但是還有兩個關鍵問題無法解決,1、我們無法知道非同步方法何時執行完畢;2、必須控制主執行緒上的button物件的enabled屬性,也就是說在非阻塞模式下不能對同一個button反覆的單擊。

我們注意到BeginInvoke方法所接受的另外兩個可選引數,第一個引數就是我們所感興趣的,它接受型別為AsyncCallBack型別的方法 - CallBack1!當非同步方法呼叫結束時會自動呼叫這個回撥方法。回撥方法實際上是在Win32開發時代很多Windows API函式經常用到的,某些API函式需要傳遞一個回撥函式的地址,當系統API執行完畢後自動執行自定義的回撥函式。

回撥方法傳遞過來一個IAsyncResult類行的引數ar,它表示非同步方法操作的狀態,IAsyncResult有一個非常有用的屬性IsCompleted,我們可以根據這個屬性的值來判斷非同步方法操作是否結束,繼而EndInvoke非同步方法。CallBack1方法也許應該這麼寫:
        private void CallBack1(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                simpleDelegate1.EndInvoke(ar);
                MessageBox.Show("Asynchronous Method Invocation is OK.");
            }
        }

但是執行結果會發現,MessageBox訊息框並不是模式化的!顯示訊息框的同時,主窗體仍然可以獲得焦點,窗體的物件也同樣也可以操作。原來,這是在另一個獨立執行緒上執行的MessageBox,而並不是在主執行緒上。那麼我們該如何顯示一個模式化主窗體的訊息框呢?答案很簡單,必須實施執行緒間操作,這裡還是要用到委託。我們建立一個要在主執行緒上執行的方法,並且為這個方法定義一個委託。然後必須檢測控制元件的InvokeRequired屬性,在我們的例子中就是this,也就是主窗體Form1,這樣做的目的是判斷呼叫執行緒是否和主執行緒位於同一執行緒中,若不是同一執行緒,必須用Invoke將方法封裝起來以便在主執行緒中執行。程式碼如下:
        private void CallBack1(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                simpleDelegate1.EndInvoke(ar);
                MethodInvoker updateUI = delegate
                   {
                       MessageBox.Show(this, "Asynchronous Method Invocation is OK.");
                   };
                if (this.InvokeRequired)
                {
                    this.Invoke(updateUI);
                }
                else
                {
                    updateUI();
                }
            }
        }

上一篇的博文中關於MethodInvoker的使用並不是這樣的啊?沒錯!這種形式在.Net 2.0下是新增的,MethodInvoker這句程式碼的意思是委託的型別以及實際方法的內容一次性定義完成。實際上下邊的程式碼與前述MethodInvoker寫法完全等同:

        MethodInvoker updateUI;

        private void ShowMsg()
        {
            MessageBox.Show(this, "Asynchronous Method Invocation is OK.");
        }

        private void CallBack1(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                simpleDelegate1.EndInvoke(ar);
                updateUI = new MethodInvoker(ShowMsg);
                if (this.InvokeRequired)
                {
                    this.Invoke(updateUI);
                }
                else
                {
                    updateUI();
                }
            }
        }

挺有意思的吧?有興趣的朋友不妨試一試。

第一個關鍵問題我們已經順利解決,那麼關於button的狀態設定問題其實到這裡我們也已經解決了,因為我們已經可以做到在一個執行緒上訪問另一個執行緒的物件及方法了。完整的程式碼如下:

        //呼叫同一個耗時的方法
        private void a()
        {
            Thread.Sleep(10000);
        }

        //阻塞式呼叫
        private void button1_Click(object sender, EventArgs e)
        {
            a();
            MessageBox.Show("button1_Click will return!");
        }

        //非同步方法呼叫 方法1
        private MethodInvoker simpleDelegate1;

        private void button2_Click(object sender, EventArgs e)
        {
            button2.Enabled = false;
            simpleDelegate1 = new MethodInvoker(a);
            simpleDelegate1.BeginInvoke(CallBack1, null);
            MessageBox.Show("button2_Click will return!");
        }

        private void CallBack1(IAsyncResult ar)
        {
            if (ar.IsCompleted)
            {
                simpleDelegate1.EndInvoke(ar);
                MethodInvoker updateUI = delegate
                   {
                       MessageBox.Show(this, "Asynchronous Method Invocation is OK.");
                       button2.Enabled = true;
                   };
                if (this.InvokeRequired)
                {
                    this.Invoke(updateUI);
                }
                else
                {
                    updateUI();
                }
            }
        }

我們已經充分介紹了非同步方法呼叫的使用,在下一篇博文中我們將繼續探討.Net多執行緒的新寵兒BackgroundWorker!

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/14325734/viewspace-422903/,如需轉載,請註明出處,否則將追究法律責任。

相關文章