C# BeginInvoke和EndInvoke方法

轉載自:BeginInvoke和EndInvoke方法html

IDE:Visual Studio 2008c#

本系列教程主要包括以下內容:
1. BeginInvoke和EndInvoke方法windows

2. Thread類併發

3. 線程池dom

4. 線程同步基礎異步

5. 死鎖async

6. 線程同步的7種方法性能

7. 如何在線程中訪問GUI組件spa

1、線程概述

在操做系統中一個進程至少要包含一個線程,而後,在某些時候須要在同一個進程中同時執行多項任務,或是爲了提供程序的性能,將要執行的任務分解成多個子任務執行。這就須要在同一個進程中開啓多個線程。咱們使用C#編寫一個應用程序(控制檯或桌面程序均可以),而後運行這個程序,並打開windows任務管理器,這時咱們就會看到這個應用程序中所含有的線程數,以下圖所示。
操作系統

若是任務管理器沒有「線程數」列,能夠【查看】>【選擇列】來顯示「線程計數」列。從上圖能夠看出,幾乎全部的進程都擁有兩個以上的線程。從而能夠看出,線程是提供應用程序性能的重要手段之一,尤爲在多核CPU的機器上尤其明顯。

2、用委託(Delegate)的BeginInvoke和EndInvoke方法操做線程

在C#中使用線程的方法不少,使用委託的BeginInvoke和EndInvoke方法就是其中之一。BeginInvoke方法可使用線程異步地執行委託所指向的方法。而後經過EndInvoke方法得到方法的返回值(EndInvoke方法的返回值就是被調用方法的返回值),或是肯定方法已經被成功調用。咱們能夠經過四種方法從EndInvoke方法來得到返回值。

3、直接使用EndInvoke方法來得到返回值

當使用BeginInvoke異步調用方法時,若是方法未執行完,EndInvoke方法就會一直阻塞,直到被調用的方法執行完畢。以下面的代碼所示:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace MyThread
{
    class Program
    {
         private static int newTask(int ms)
         {
             Console.WriteLine("任務開始");
             Thread.Sleep(ms);
             Random random = new Random();
             int n = random.Next(10000);
             Console.WriteLine("任務完成");
             return n;
         }
         private delegate int NewTaskDelegate(int ms);
         static void Main(string[] args)
         {
             NewTaskDelegate task = newTask;
             IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
             // EndInvoke方法將被阻塞2秒
             int result = task.EndInvoke(asyncResult);           
             Console.WriteLine(result);
         }
     }
}

在運行上面的程序後,因爲newTask方法經過Sleep延遲了2秒,所以,程序直到2秒後才輸出最終結果(一個隨機整數)。若是不調用EndInvoke方法,程序會當即退出,這是因爲使用BeginInvoke建立的線程都是後臺線程,這種線程一但全部的前臺線程都退出後(其中主線程就是一個前臺線程),無論後臺線程是否執行完畢,都會結束線程,並退出程序。關於前臺和後臺線程的詳細內容,將在後面的部分講解。

讀者可使用上面的程序作如下實驗。首先在Main方法的開始部分加入以下代碼:

Thread.Sleep(10000);

以使Main方法延遲10秒鐘再執行下面的代碼,而後按Ctrl+F5運行程序,並打開企業管理器,觀察當前程序的線程數,假設線程數是4,在10秒後,線程數會增至5,這是由於調用BeginInvoke方法時會創建一個線程來異步執行newTask方法,所以,線程會增長一個。

4、使用IAsyncResult asyncResult屬性來判斷異步調用是否完成

雖然上面的方法能夠很好地實現異步調用,可是當調用EndInvoke方法得到調用結果時,整個程序就象死了同樣,這樣作用戶的感受並不會太好,所以,咱們可使用asyncResult來判斷異步調用是否完成,並顯示一些提示信息。這樣作能夠增長用戶體驗。代碼以下:

static void Main(string[] args)
{
     NewTaskDelegate task = newTask;
     IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
     while (!asyncResult.IsCompleted)
     {
        Console.Write("*");
        Thread.Sleep(100);
     }
     // 因爲異步調用已經完成,所以, EndInvoke會馬上返回結果
     int result = task.EndInvoke(asyncResult);           
     Console.WriteLine(result);
}

上面代碼的執行結果以下圖所示。

因爲是異步,因此「*」可能會在「任務開始」前輸出,如上圖所示。

5、使用WaitOne方法等待異步方法執行完成
使用WaitOne方法是另一種判斷異步調用是否完成的方法。代碼以下:

static void Main(string[] args)
{
     NewTaskDelegate task = newTask;
     IAsyncResult asyncResult = task.BeginInvoke(2000, null, null);
     while (!asyncResult.AsyncWaitHandle.WaitOne(100, false))
     {
          Console.Write("*");              
     }
     int result = task.EndInvoke(asyncResult);
     Console.WriteLine(result);
}

WaitOne的第一個參數表示要等待的毫秒數,在指定時間以內,WaitOne方法將一直等待,直到異步調用完成,併發出通知,WaitOne方法才返回true。當等待指定時間以後,異步調用仍未完成,WaitOne方法返回false,若是指定時間爲0,表示不等待,若是爲-1,表示永遠等待,直到異步調用完成。

6、使用回調方式返回結果
上面介紹的幾種方法實際上只至關於一種方法。這些方法雖然能夠成功返回結果,也能夠給用戶一些提示,但在這個過程當中,整個程序就象死了同樣(若是讀者在GUI程序中使用這些方法就會很是明顯),要想在調用的過程當中,程序仍然能夠正常作其它的工做,就必須使用異步調用的方式。下面咱們使用GUI程序來編寫一個例子,代碼以下:

private delegate int MyMethod();
private int method()
{
     Thread.Sleep(10000);
     return 100;
}
private void MethodCompleted(IAsyncResult asyncResult)
{
     if (asyncResult == null) return;
     textBox1.Text = (asyncResult.AsyncState as
     MyMethod).EndInvoke(asyncResult).ToString();
}
private void button1_Click(object sender, EventArgs e)
{
     MyMethod my = method;
     IAsyncResult asyncResult = my.BeginInvoke(MethodCompleted, my);
}

要注意的是,這裏使用了BeginInvoke方法的最後兩個參數(若是被調用的方法含有參數的話,這些參數將做爲BeginInvoke的前面一部分參數,若是沒有參數,BeginInvoke就只有兩個參數了)。第一個參數是回調方法委託類型,這個委託只有一個參數,就是IAsyncResult,如MethodCompleted方法所示。當method方法執行完後,系統會自動調用MethodCompleted方法。BeginInvoke的第二個參數須要向MethodCompleted方法中傳遞一些值,通常能夠傳遞被調用方法的委託,如上面代碼中的my。這個值可使用IAsyncResult.AsyncState屬性得到。

因爲上面的代碼經過異步的方式訪問的form上的一個textbox,所以,須要按ctrl+f5運行程序(不能直接按F5運行程序,不然沒法在其餘線程中訪問這個textbox,關於若是在其餘線程中訪問GUI組件,並在後面的部分詳細介紹)。並在form上放一些其餘的可視控件,然在點擊button1後,其它的控件仍然可使用,就象什麼事都沒有發生過同樣,在10秒後,在textbox1中將輸出100。

7、其餘組件的BeginXXX和EndXXX方法
在其餘的.net組件中也有相似BeginInvoke和EndInvoke的方法,如System.Net.HttpWebRequest類的BeginGetResponse和EndGetResponse方法,下面是使用這兩個方法的一個例子:

private void requestCompleted(IAsyncResult asyncResult)
        {
            if (asyncResult == null) return;
            System.Net.HttpWebRequest hwr = asyncResult.AsyncState as System.Net.HttpWebRequest;
            System.Net.HttpWebResponse response =
                (System.Net.HttpWebResponse)hwr.EndGetResponse(asyncResult);
            System.IO.StreamReader sr = new
                System.IO.StreamReader(response.GetResponseStream());
            textBox1.Text = sr.ReadToEnd();
        }
        private delegate System.Net.HttpWebResponse RequestDelegate(System.Net.HttpWebRequest request);
        private void button1_Click(object sender, EventArgs e)
        {
            System.Net.HttpWebRequest request =
            (System.Net.HttpWebRequest)System.Net.WebRequest.Create("http://www.cnblogs.com");
            IAsyncResult asyncResult = request.BeginGetResponse(requestCompleted, request);
        }


其餘參考

c#中的BeginInvoke和EndEndInvoke

相關文章
相關標籤/搜索