[C#基礎]c#中的BeginInvoke和EndEndInvoke

MSDN原文地址:https://msdn.microsoft.com/en-us/library/2e08f6yc(v=vs.110).aspx

.Net framework可以讓你異步調用任何方法。爲達這樣的目的,你可以定義一個與你要調用的方法的簽名相同的委託。公共語言運行時將自動爲該委託定義與簽名相同的BeginInvok和EndInvoke方法。

異步委託調用BeginInvok和EndInvoke方法,但在.NET Compact Framework中並不支持。

BeginInvoke方法觸發你的異步方法,它和你想要執行的異步方法有相同的參數。另外還有兩個可選參數,第一個是AsyncCallback委託是異步完成的回調方法。第二個是用戶自定義對象,該對象將傳遞到回調方法中。BeginInvoke立即返回並且不等待完成異步的調用(繼續執行該下面的代碼,不需要等待)。BeginInvoke返回IAsyncResult接口,可用於檢測異步調用的過程。

通過EndInvoke方法檢測異步調用的結果。如果異步調用尚未完成,EndInvoke將阻塞調用線程,直到它完成。EndInvoke參數包括out和ref參數。

下面代碼演示使用BeginInvoke和EndInvoke進行異步調用的四種常見方式。在調用BeginInvoke可以做以下工作:

  • 做一些其他操作,然後調用EndInvoke方法阻塞線程直到該方法完成。
  • 使用IAsyncResult.AsyncWaitHandle屬性,使用它的WaitOne方法阻塞線程直到收到WaitHandle信號,然後調用EndInvoke。
  • 檢查BeginInvoke返回值IAsyncResult的狀態來決定方法是否完成,然後調用EndInvoke方法。
  • 通過在BeginInvoke方法中傳遞該委託,在回調方法中調用該委託的EenInvoke方法。

注意

無論你怎麼使用,都必須調用EndInvoke方法結束你的異步調用。

下面通過模擬一個耗時的操作,實現上面說的那四種情況。

複製代碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Wolfy.AsyncDemo
{
    /// <summary>
    /// 委託必須和要調用的異步方法有相同的簽名
    /// </summary>
    /// <param name="callDuration"></param>
    /// <param name="threadId"></param>
    /// <returns></returns>
    public delegate string AsyncMethodCaller(int callDuration, out int threadId);
    class Program
    {       
        static void Main(string[] args)
        {
            
        }
        static string TestMethod(int callDuration, out int threadId)
        {
            Console.WriteLine("Test method begins:");
            //睡一會 模擬耗時操作
            Thread.Sleep(callDuration);
            threadId = Thread.CurrentThread.ManagedThreadId;
            return string.Format("My call time was {0}.", callDuration.ToString());
        }
    }
}
複製代碼

情況一:通過EndInovke阻塞線程,直到異步調用結束。

複製代碼
        static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            //調用EndInvoke方法,等待異步嗲用完成,並得到結果。
            string returnValue = caller.EndInvoke(out threadId, result);
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
複製代碼

結果

情況二:通過WaitHandle屬性阻塞線程。

你可以獲得BeginInvoke的返回值的WaitHandle,並使用它的AsyncWaitHanlde屬性。WaitHandle信號異步完成時,你可以通過調用WaitOne方法等待。

如果你使用WaitHandle,你可以在之前或者異步調用完成後進行其他的操作。但之後必須使用EndInvoke檢查結果。

注意

當你調用EndInvoke方法時,等待句柄並不會自動關閉。如果你釋放等待處理的所有引用,當垃圾回收等待句柄是,系統資源將被釋放。一旦你完成使用等待句柄,通過WaitHandle的close方法,一次性顯示關閉,這時的垃圾回收效率更高。

複製代碼
        static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);
            //等待WaitHandle信號
            result.AsyncWaitHandle.WaitOne();
            //調用EndInvoke方法,等待異步嗲用完成,並得到結果。
            string returnValue = caller.EndInvoke(out threadId, result);
            //關閉等待句柄
            result.AsyncWaitHandle.Close();
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
複製代碼

情況三:檢查BeginInvoke返回結果的狀態。

可以通過BeginInvoke的返回結果的IsCompleted屬性檢查異步是否完成。你可以在異步沒有完成的時候做其他的操作。

複製代碼
       static void Main(string[] args)
        {
            int threadId;
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            IAsyncResult result = caller.BeginInvoke(3000, out threadId, null, null);
            Thread.Sleep(0);
            Console.WriteLine("Main thread {0} does some work.", Thread.CurrentThread.ManagedThreadId);
            while (result.IsCompleted == false)
            {
                Thread.Sleep(250);
                Console.WriteLine("*");
            }
            //調用EndInvoke方法,等待異步嗲用完成,並得到結果。
            string returnValue = caller.EndInvoke(out threadId, result);
            //關閉等待句柄
            result.AsyncWaitHandle.Close();
            Console.WriteLine("The call executed on thread {0},with return value {1}.", threadId, returnValue);
            Console.Read();
        }
複製代碼

情況四:通過在回調方法中。

如果需要在異步完成後需要做一些其他的操作,你可以在異步完成時執行一個回調方法。在該回調方法中做處理。

首先需要定義一個回調方法。

複製代碼
     static void Main(string[] args)
        {
            AsyncMethodCaller caller = new AsyncMethodCaller(TestMethod);
            // The threadId parameter of TestMethod is an out parameter, so
            // its input value is never used by TestMethod. Therefore, a dummy
            // variable can be passed to the BeginInvoke call. If the threadId
            // parameter were a ref parameter, it would have to be a class-
            // level field so that it could be passed to both BeginInvoke and 
            // EndInvoke.
            int dummy = 0;
            // Initiate the asynchronous call, passing three seconds (3000 ms)
            // for the callDuration parameter of TestMethod; a dummy variable 
            // for the out parameter (threadId); the callback delegate; and
            // state information that can be retrieved by the callback method.
            // In this case, the state information is a string that can be used
            // to format a console message.
            IAsyncResult result = caller.BeginInvoke(3000, out dummy, TestMethodCallback, "The call executed on thread {0},with return value {1}.");
            Console.Read();
        }
複製代碼

總結

在項目中遇到的有參數又返回值的情況,如何在回調方法中拿到委託和傳遞的參數,當時卡這裏了,後來查看情況四的情況,才得以解決。這裏也再學習一下。


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