委托的BeginInvoke和EndInvoke方法


  .NET Framework 允許異步調用任何方法,為了實現異步調用目標,需要定義與被調用方法具有相同簽名的委托。公共語言運行時會自動使用適當的簽名為該委托定義 BeginInvoke 和 EndInvoke 方法,也就是說委托的 BeginInvoke 和 EndInvoke 方法是自動生成的,無需定義。所謂的異步調用,指的是在新線程中執行被調用的方法。
  BeginInvoke 方法啟動異步調用, 該方法與要異步執行的方法具有相同的參數,還有另外兩個可選參數。第一個參數是一個 AsyncCallback 委托,該委托引用在異步調用完成時要調用的方法。 第二個參數是一個用戶定義的對象,該對象將信息傳遞到回調方法。BeginInvoke 立即返回,不等待異步調用完成。 BeginInvoke 返回一個 IAsyncResult,后者可用於監視異步調用的進度。
  采用BeginInvoke 和 EndInvoke實現方法的異步調用共有四種方式,下面用演示代碼分別說明。代碼示例演示異步調用一個長時間運行的方法 TestMethod 的各種方式。 TestMethod 方法會顯示一條控制台消息,說明該方法已開始處理,方法所在的線程,休眠了幾秒鍾,然后結束。 TestMethod 有一個 out 參數,用於說明此類參數添加到 BeginInvoke 和EndInvoke 的簽名中的方式,可以按同樣的方式處理 ref 參數。

        public static string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("方法的執行線程ID: {0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }

  定義相應的委托類型

 public delegate string AsyncDelegate(int  callDuration, out int threadId);

1 使用 EndInvoke阻塞調用線程,直到異步調用結束

  異步執行方法的最簡單方式是通過調用委托的 BeginInvoke 方法來開始執行方法,可以在在調用線程上執行一些其他操作,然后調用委托的 EndInvoke 方法。 EndInvoke 會阻塞調用線程,直到異步調用完成后才返回。

   

namespace TestInvoke
{
    public delegate string AsyncDelegate(int  callDuration, out int threadId);
    class Program
    {
        static void Main(string[] args)
        {
            //輸出主線程ID
            Console.WriteLine("主線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //創建委托
            AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
            int nThreadID = 0;

            //異步執行TestMethod方法
            IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);
            //阻塞調用線程
            asyncDel.EndInvoke(out nThreadID, result);


            Console.ReadKey();
        }

        public static string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("方法的執行線程ID: {0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }
    }
}

 

  調用線程會被阻塞3秒鍾,程序輸出為:

  

2 使用 WaitHandle 等待異步調用

  使用由 BeginInvoke 返回的 IAsyncResult 的 AsyncWaitHandle 屬性來獲取 WaitHandle。 異步調用完成時, WaitHandle 會收到信號,可以通過調用 WaitOne 方法等待它。

  如果您使用 WaitHandle時,在調用 EndInvoke 檢索結果之前,還可以執行其他處理。

  

        static void Main(string[] args)
        {
            //輸出主線程ID
            Console.WriteLine("主線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //創建委托
            AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
            int nThreadID = 0;

            //異步執行TestMethod方法
            IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);
            //阻塞調用線程
            WaitHandle handle = result.AsyncWaitHandle;
            handle.WaitOne();

            //其他操作

            //終止異步調用,通過返回值取得調用結果
            string returnValue = asyncDel.EndInvoke(out nThreadID, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                nThreadID, returnValue);

            Console.ReadKey();
        }

3輪詢異步調用完成

  可以使用由 BeginInvoke 返回的 IAsyncResult 的 IsCompleted 屬性來判斷異步調用何時完成。

 

        static void Main(string[] args)
        {
            //輸出主線程ID
            Console.WriteLine("主線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //創建委托
            AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
            int nThreadID = 0;

            //異步執行TestMethod方法
            IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID, null, null);

            //輪詢異步執行狀態
            while (true)
            {
                if (result.IsCompleted)
                {
                    break;
                }
                Thread.Sleep(1000);
            }

            //終止異步調用,通過返回值取得調用結果
            string returnValue = asyncDel.EndInvoke(out nThreadID, result);

            Console.WriteLine("The call executed on thread {0}, with return value \"{1}\".",
                nThreadID, returnValue);

            Console.ReadKey();
        }

 

 4 異步調用完成時執行回調方法 

  如果調用的線程不需要的異步執行函數的返回值,則可以在調用完成時執行回調方法,回調方法在 ThreadPool 里的異步線程上執行。

若要使用回調方法,必須將表示回調方法的 AsyncCallback 委托傳遞給 BeginInvoke。 也可以傳遞包含回調方法要使用的信息的對象。在回調方法中,可以將 IAsyncResult(回調方法的唯一參數)強制轉換為 AsyncResult 對象。 然后,可以使用AsyncResult .AsyncDelegate 屬性獲取已用於啟動調用的委托,以便可以調用 EndInvoke。

  • TestMethod 的 threadId 參數為 out 參數,因此 TestMethod 從不使用該參數的輸入值。  如果 threadId 參數為 ref 參數,則該變量必須為類級別字段,這樣才能同時傳遞給 BeginInvoke 和 EndInvoke。

  • 傳遞給 BeginInvoke 的狀態信息是一個格式字符串,所以狀態信息必須強制轉換為正確的類型才能被使用。

  • 回調在 ThreadPool 線程上執行。 ThreadPool 線程是后台線程,這些線程不會在主線程結束后保持應用程序的運行,因此示例的主線程必須休眠足夠長的時間以便回調完成。

  

       static void Main(string[] args)
        {
            //輸出主線程ID
            Console.WriteLine("主線程ID:{0}", Thread.CurrentThread.ManagedThreadId);
            //創建委托
            AsyncDelegate asyncDel = new AsyncDelegate(TestMethod);
            int nThreadID = 0;

            //異步執行TestMethod方法,使用回調函數並傳入state參數
            IAsyncResult result = asyncDel.BeginInvoke(3000, out nThreadID,
                new AsyncCallback(AsyncCallCompleted), "測試參數傳遞");

            Console.ReadKey();
        }

        public static void AsyncCallCompleted(IAsyncResult ar)
        {
            Console.WriteLine("AsyncCallCompleted執行線程ID:{0}",Thread.CurrentThread.ManagedThreadId);

            //獲取委托對象
            System.Runtime.Remoting.Messaging.AsyncResult result = (System.Runtime.Remoting.Messaging.AsyncResult)ar;
            AsyncDelegate asyncDel = (AsyncDelegate)result.AsyncDelegate;

            //獲取BeginInvoke傳入的的state參數
            string strState = (string)ar.AsyncState;
            Console.WriteLine("傳入的字符串是{0}",strState);

            //結束異步調用
            int nThreadID;
            string strResult = asyncDel.EndInvoke(out nThreadID, ar);

            Console.WriteLine("\nThe call executed on thread {0}, with return value \"{1}\".",
               nThreadID, strResult);
        }

        public static string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("TestMethod方法的執行線程ID: {0}", Thread.CurrentThread.ManagedThreadId);
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return String.Format("My call time was {0}.", callDuration.ToString());
        }

 

  輸出結果為

  

 總結

    除了第4種采用回調函數的方式外,其他三種方式均會阻塞調用線程。

 


免責聲明!

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



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