天天看點

.Net的異步機制(APM核心:IAsyncResult) - 下篇

異步的核心: IAsyncResult

Asynchronous Programming Model

整個異步調用過程中都是圍繞IAsyncResult來進行的,大家可以看看上篇文章的例子,BeginXXX 傳回這個對象,EndXXX接收這個對象來結束目前異步對象,下面我們來看看IAsyncResult 接口成員/和實作此接口的AsyncResult類成員(其中有些在上篇中已經涉及到)

IAsyncResult接口

public interface IAsyncResult
    {
        WaitHandle AsyncWaitHandle { get; } //阻塞一個線程,直到一個或多個同步對象接收到信号
        Boolean IsCompleted { get; } //判讀目前異步是否完成
        Object AsyncState { get; } //擷取額外的參數值,請看上一篇文章的Code 4.3
        Boolean CompletedSynchronously { get; } //幾乎沒有使用
    }      

AsyncResult類

public class AsyncResult : IAsyncResult, IMessageSink
    {
        //IAsyncResult 的實作      
        public virtual WaitHandle AsyncWaitHandle { get; }
        public virtual bool IsCompleted { get; }
        public virtual object AsyncState { get; }
        public virtual bool CompletedSynchronously { get; }

        // 其他一些重要的屬性
        public bool EndInvokeCalled { get; set; } //檢驗是否調用了EndInvoke()
        public virtual object AsyncDelegate { get; } //擷取原始的委托對象,可檢視上一篇文章中的Code 4.1/4.2/5
    }      

注意:基本上都是隻讀屬性

下面我們來看看異步的執行順序,并回顧下 IAsyncResult 下各個屬性的應用,如果還是不熟悉請看前2篇文章.

Code 1:

class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);

            AsyncTest test = new AsyncTest();
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
            //使用回調函數
            AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
            IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);

            DoAntherJob();
            Console.ReadLine(); // 讓黑屏等待,不會直接關閉..
        }

        //開始其他工作.
        static void DoAntherJob()
        {
            Thread.Sleep(1000);//需要1秒才能完成這個工作,注1
            Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
        }

        static void OnSalaryCallback(IAsyncResult asyncResult)
        {
            //通過AsyncState 擷取額外的參數.
            decimal para = (int)asyncResult.AsyncState;

            //通過AsyncDelegate 擷取原始的委托對象
            AsyncResult obj = (AsyncResult)asyncResult;
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;

            if (asyncResult.IsCompleted)// 判讀是否已經調用完成
                Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);

            decimal val = del.EndInvoke(asyncResult);

            Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
        }
    }

    public class AsyncTest
    {
        public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 對應YearlySalary方法
        public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
        {
            //模拟耗時/複雜的邏輯計算.
            Thread.Sleep(3000);//等待3秒,注2
            return salary * monthCount + bonus;
        }
    }      
.Net的異步機制(APM核心:IAsyncResult) - 下篇

圖1

我們看到DoAntherJob 比異步YearlySalary快2秒,看代碼中(注1)和(注2),兩個線程的執行結果

接下來,我們說說AsyncWaitHandle 屬性. 他傳回WaitHandle對象(System.Threading.WaitHandle), 他有3個重要的方法. WaitOne / WaitAny / WaitAll ,我們先來說下WaitOne,在Code1代碼基礎上隻是增加了下面紅色部分.

1,WaitOne

Code 1.1

IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000); //阻礙目前線程,直到異步調用結束. ar.AsyncWaitHandle.WaitOne(); //開始其他工作. DoAntherJob();

.Net的異步機制(APM核心:IAsyncResult) - 下篇

圖1.1

執行輸出,對比圖1我們可以看到執行的次序不一樣了(看時間),調用WaitOne,會阻礙目前線程,直到異步完成,才釋放目前的線程, WaitOne 提供了時間的重載版本WaitOne(int millisecondsTimeout)/ WaitOne(TimeSpan timeout);來判斷阻礙的時間.無參的版本是無限等待的(直到異步調用結束)

2, WaitAll

我們在Code1的代碼基礎上加上Hello的異步調用(使Main提供多個異步調用),注意紅色部分.

Code 1.2

1 class Program
 2     {
 3         static void Main(string[] args)
 4         {
 5             Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
 6 
 7             AsyncTest test = new AsyncTest();
 8             MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
 9             MyThirdAsyncCode.AsyncTest.AsyncEventHandler asy = test.Hello;
10 
11             IAsyncResult salayAsyc = del.BeginInvoke(100000, 15, 100000, OnSalaryCallback, null);
12             IAsyncResult helloAsyc = asy.BeginInvoke("Hello Andy", OnHelloCallback, null);
13             //把所有異步的句柄儲存到WaitHandle 對象中
14             WaitHandle[] handles = { salayAsyc.AsyncWaitHandle, helloAsyc.AsyncWaitHandle };
15             //阻礙目前線程,直到所有異步調用結束.
16             WaitHandle.WaitAll(handles);
17 
18             //開始其他工作.
19             DoAntherJob();
20             Console.ReadLine(); // 讓黑屏等待,不會直接關閉..
21         }
22         static void DoAntherJob()
23         {
24             Thread.Sleep(1000);//需要1秒才能完成這個工作,注1
25             Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
26         }
27         static void OnSalaryCallback(IAsyncResult asyncResult)
28         {
29             //通過AsyncDelegate 擷取原始的委托對象
30             AsyncResult obj = (AsyncResult)asyncResult;
31             MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
32 (MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
33 
34             if (asyncResult.IsCompleted)// 判讀是否已經調用完成
35                 Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
36 
37             decimal val = del.EndInvoke(asyncResult);
38             Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
39         }
40 
41         static void OnHelloCallback(IAsyncResult asyncResult)
42         {
43             //通過AsyncDelegate 擷取原始的委托對象
44             AsyncResult obj = (AsyncResult)asyncResult;
45             MyThirdAsyncCode.AsyncTest.AsyncEventHandler del = 
46 (MyThirdAsyncCode.AsyncTest.AsyncEventHandler)obj.AsyncDelegate;
47 
48             if (asyncResult.IsCompleted)// 判讀是否已經調用完成
49                 Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
50 
51             string val = del.EndInvoke(asyncResult);
52             Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
53         }
54     }
55 
56     public class AsyncTest
57     {
58         public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 對應YearlySalary方法
59         public delegate string AsyncEventHandler(string name); // 對應Hello 方法
60         public string Hello(string name)
61         {
62             //模拟耗時/複雜的邏輯計算.等待5秒
63             Thread.Sleep(5000);
64             return "Hello:" + name;
65         }
66         public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
67         {
68             //模拟耗時/複雜的邏輯計算.
69             Thread.Sleep(3000);//等待3秒
70             return salary * monthCount + bonus;
71         }
72     }      

View Code

.Net的異步機制(APM核心:IAsyncResult) - 下篇

圖1.2

從圖1.2中可以看出,WaitAll會阻礙目前線程(主線程#10),等待所有異步的對象都執行完畢(耗時最長的異步),才釋放目前的線程,WaitAll/WaitAny的重載版本和WaitOne一樣.

3, WaitAny

和WaitAll 基本上是一樣的.我們可以使用 WaitAny 來指定某個/某幾個委托先等待,修改Code1.2紅色部分,使用WaitAny.

Code1.3 //把salayAsyc異步的句柄儲存到WaitHandle 對象中 WaitHandle[] handles = { salayAsyc.AsyncWaitHandle }; //阻礙目前線程,直到所有異步調用結束. WaitHandle.WaitAny(handles);

.Net的異步機制(APM核心:IAsyncResult) - 下篇

圖1.3

我們阻礙了DoAntherJob(#10)線程,直到Salary異步調用計算完成.同樣我們可以巧用這三個方法來改變我們方法執行的順序.

釋放資源

Code2:

static void OnSalaryCallback(IAsyncResult asyncResult)
        {
            //通過AsyncDelegate 擷取原始的委托對象
            AsyncResult obj = (AsyncResult)asyncResult;
            MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = 
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;

            decimal val = del.EndInvoke(asyncResult);
            asyncResult.AsyncWaitHandle.Close();//顯示的釋放資源
        }      

當開始調用BeginXXX後,就會建立一個新的AsyncResult對象.這個對象會構造一個WaitHandle句柄(通過AsyncWaitHandle通路),當我們EndXXX後,并不會馬上關閉這個句柄,而是等待垃圾收集器來關閉,這時候我們最後在調用EndXXX完成後,顯示的關閉這個句柄.

說到這裡,我們基本上把異步方法都解釋一遍,下面我們來看看重構的異步對象,我們也可以細細體會異步對象的内部執行代碼..下面Code3.1/3.2/3.3代碼來自Jeffery Richard大師的Power Threading類庫,具體可檢視http://msdn.microsoft.com/en-us/magazine/cc163467.aspx

重構的異步對象

1步,構造一個内部無參的AsyncResultNoResult對象,繼承IAsyncResult接口(保留原創的注釋)

Code3.1

1 internal class AsyncResultNoResult : IAsyncResult
 2     {
 3         // Fields set at construction which never change while 
 4         // operation is pending
 5         private readonly AsyncCallback m_AsyncCallback;
 6         private readonly Object m_AsyncState;
 7 
 8         // Fields set at construction which do change after 
 9         // operation completes
10         private const Int32 c_StatePending = 0; 
11         private const Int32 c_StateCompletedSynchronously = 1;
12         private const Int32 c_StateCompletedAsynchronously = 2;
13         private Int32 m_CompletedState = c_StatePending;
14 
15         // Field that may or may not get set depending on usage
16         private ManualResetEvent m_AsyncWaitHandle;
17 
18         // Fields set when operation completes
19         private Exception m_exception;
20 
21         public AsyncResultNoResult(AsyncCallback asyncCallback, Object state)
22         {
23             m_AsyncCallback = asyncCallback;
24             m_AsyncState = state;
25         }
26 
27         public void SetAsCompleted(
28            Exception exception, Boolean completedSynchronously)
29         {
30             // Passing null for exception means no error occurred. 
31             // This is the common case
32             m_exception = exception;
33 
34             // The m_CompletedState field MUST be set prior calling the callback
35             Int32 prevState = Interlocked.Exchange(ref m_CompletedState,
36                completedSynchronously ? c_StateCompletedSynchronously :
37                c_StateCompletedAsynchronously);
38             if (prevState != c_StatePending)
39                 throw new InvalidOperationException(
40                     "You can set a result only once");
41 
42             // If the event exists, set it
43             if (m_AsyncWaitHandle != null) m_AsyncWaitHandle.Set();
44 
45             // If a callback method was set, call it
46             if (m_AsyncCallback != null) m_AsyncCallback(this);
47         }
48 
49         public void EndInvoke()
50         {
51             // This method assumes that only 1 thread calls EndInvoke 
52             // for this object
53             if (!IsCompleted)
54             {
55                 // If the operation isn't done, wait for it
56                 AsyncWaitHandle.WaitOne();
57                 AsyncWaitHandle.Close();
58                 m_AsyncWaitHandle = null;  // Allow early GC
59             }
60 
61             // Operation is done: if an exception occured, throw it
62             if (m_exception != null) throw m_exception;
63         }
64 
65         Implementation of IAsyncResult
66     }      

View Code

2步,繼承AsyncResultNoResult對象,并且為他提供傳回值和泛型的通路

Code3.2

internal class AsyncResult<TResult> : AsyncResultNoResult
    {
        // Field set when operation completes
        private TResult m_result = default(TResult);

        public AsyncResult(AsyncCallback asyncCallback, Object state) :
            base(asyncCallback, state) { }

        public void SetAsCompleted(TResult result,
           Boolean completedSynchronously)
        {
            // Save the asynchronous operation's result
            m_result = result;

            // Tell the base class that the operation completed 
            // sucessfully (no exception)
            base.SetAsCompleted(null, completedSynchronously);
        }

        new public TResult EndInvoke()
        {
            base.EndInvoke(); // Wait until operation has completed 
            return m_result;  // Return the result (if above didn't throw)
        }
    }      

3步,模拟長時間的異步工作

Code3.3:

internal sealed class LongTask
    {
        private Int32 m_ms;  // Milliseconds;

        public LongTask(Int32 seconds)
        {
            m_ms = seconds * 1000;
        }

        // Synchronous version of time-consuming method
        public DateTime DoTask()
        {
            Thread.Sleep(m_ms);  // Simulate time-consuming task
            return DateTime.Now; // Indicate when task completed
        }

        // Asynchronous version of time-consuming method (Begin part)
        public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
        {
            // Create IAsyncResult object identifying the 
            // asynchronous operation
            AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
               callback, state);

            // Use a thread pool thread to perform the operation
            ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);

            return ar;  // Return the IAsyncResult to the caller
        }

        // Asynchronous version of time-consuming method (End part)
        public DateTime EndDoTask(IAsyncResult asyncResult)
        {
            // We know that the IAsyncResult is really an 
            // AsyncResult<DateTime> object
            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;

            // Wait for operation to complete, then return result or 
            // throw exception
            return ar.EndInvoke();
        }

        // Asynchronous version of time-consuming method (private part 
        // to set completion result/exception)
        private void DoTaskHelper(Object asyncResult)
        {
            // We know that it's really an AsyncResult<DateTime> object
            AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
            try
            {
                // Perform the operation; if sucessful set the result
                DateTime dt = DoTask();
                ar.SetAsCompleted(dt, false);
            }
            catch (Exception e)
            {
                // If operation fails, set the exception
                ar.SetAsCompleted(e, false);
            }
        }
    }      

引用自 :http://www.cnblogs.com/30ErLi/archive/2010/09/19/1830729.html

轉載于:https://www.cnblogs.com/wangquan0816/p/3193074.html