程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> 關於.NET >> 什麼是.Net的異步機制(APM核心IAsyncResult) - step 3

什麼是.Net的異步機制(APM核心IAsyncResult) - step 3

編輯:關於.NET

異步的核心: IAsyncResult

Asynchronous Programming Model

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

IAsyncResult接口

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

AsyncResult類

1  public class AsyncResult : IAsyncResult, IMessageSink
2  {
3    //IAsyncResult 的實現   
4    public virtual WaitHandle AsyncWaitHandle { get; }
5    public virtual bool IsCompleted { get; }
6    public virtual object AsyncState { get; }
7    public virtual bool CompletedSynchronously { get; }
8
9    // 其他一些重要的屬性
10    public bool EndInvokeCalled { get; set; } //檢驗是否調用了EndInvoke()
11    public virtual object AsyncDelegate { get; } //獲取原始的委托對象,可查看上一篇文章中的Code 4.1/4.2/5
12  }

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

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

Code 1:

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      //使用回調函數
10      AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
11      IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
12
13      DoAntherJob();
14      Console.ReadLine(); // 讓黑屏等待,不會直接關閉..15    }
16
17    //開始其他工作.18    static void DoAntherJob()
19    {
20      Thread.Sleep(1000);//需要1秒才能完成這個工作,注1
21      Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
22    }
23
24    static void OnSalaryCallback(IAsyncResult asyncResult)
25    {
26      //通過AsyncState 獲取額外的參數.27      decimal para = (int)asyncResult.AsyncState;
28
29      //通過AsyncDelegate 獲取原始的委托對象
30      AsyncResult obj = (AsyncResult)asyncResult;
31      MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
32
33      if (asyncResult.IsCompleted)// 判讀是否已經調用完成
34        Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
35
36      decimal val = del.EndInvoke(asyncResult);
37
38      Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
39    }
40  }
41
42  public class AsyncTest
43  {
44    public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 對應YearlySalary方法
45    public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
46    {
47      //模擬耗時/復雜的邏輯計算.48      Thread.Sleep(3000);//等待3秒,注2
49      return salary * monthCount + bonus;
50    }
51  }

圖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();

圖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 =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
32
33      if (asyncResult.IsCompleted)// 判讀是否已經調用完成
34        Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
35
36      decimal val = del.EndInvoke(asyncResult);
37      Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
38    }
39
40    static void OnHelloCallback(IAsyncResult asyncResult)
41    {
42      //通過AsyncDelegate 獲取原始的委托對象
43      AsyncResult obj = (AsyncResult)asyncResult;
44      MyThirdAsyncCode.AsyncTest.AsyncEventHandler del =
(MyThirdAsyncCode.AsyncTest.AsyncEventHandler)obj.AsyncDelegate;
45
46      if (asyncResult.IsCompleted)// 判讀是否已經調用完成
47        Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
48
49      string val = del.EndInvoke(asyncResult);
50      Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val, Thread.CurrentThread.ManagedThreadId);
51    }
52  }
53
54  public class AsyncTest
55  {
56    public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 對應YearlySalary方法
57    public delegate string AsyncEventHandler(string name); // 對應Hello 方法
58    public string Hello(string name)
59    {
60      //模擬耗時/復雜的邏輯計算.等待5秒
61      Thread.Sleep(5000);
62      return "Hello:" + name;
63    }
64    public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
65    {
66      //模擬耗時/復雜的邏輯計算.67      Thread.Sleep(3000);//等待3秒
68      return salary * monthCount + bonus;
69    }
70  }

圖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);

圖1.3

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

釋放資源

Code2

1static void OnSalaryCallback(IAsyncResult asyncResult)
2    {
3      //通過AsyncDelegate 獲取原始的委托對象
4      AsyncResult obj = (AsyncResult)asyncResult;
5      MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
6
7      decimal val = del.EndInvoke(asyncResult);
8      asyncResult.AsyncWaitHandle.Close();//顯示的釋放資源
9    }

當開始調用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#region Implementation of IAsyncResult
66    public Object AsyncState { get { return m_AsyncState; } }
67
68    public Boolean CompletedSynchronously
69    {
70      get
71      {
72        return Thread.VolatileRead(ref m_CompletedState) ==
73          c_StateCompletedSynchronously;
74      }
75    }
76
77    public WaitHandle AsyncWaitHandle
78    {
79      get
80      {
81        if (m_AsyncWaitHandle == null)
82        {
83          Boolean done = IsCompleted;
84          ManualResetEvent mre = new ManualResetEvent(done);
85          if (Interlocked.CompareExchange(ref m_AsyncWaitHandle,
86            mre, null) != null)
87          {
88            // Another thread created this object's event; dispose
89            // the event we just created
90            mre.Close();
91          }
92          else
93          {
94            if (!done && IsCompleted)
95            {
96              // If the operation wasn't done when we created
97              // the event but now it is done, set the event
98              m_AsyncWaitHandle.Set();
99            }
100          }
101        }
102        return m_AsyncWaitHandle;
103      }
104    }
105
106    public Boolean IsCompleted
107    {
108      get
109      {
110        return Thread.VolatileRead(ref m_CompletedState) !=
111          c_StatePending;
112      }
113    }
114    #endregion
115  }

2步,繼承AsyncResultNoResult對象,並且為他提供返回值和泛型的訪問

Code3.2

1internal class AsyncResult<TResult> : AsyncResultNoResult
2  {
3    // Field set when operation completes
4    private TResult m_result = default(TResult);
5
6    public AsyncResult(AsyncCallback asyncCallback, Object state) :
7      base(asyncCallback, state) { }
8
9    public void SetAsCompleted(TResult result,
10      Boolean completedSynchronously)
11    {
12      // Save the asynchronous operation's result
13      m_result = result;
14
15      // Tell the base class that the operation completed
16      // sucessfully (no exception)
17      base.SetAsCompleted(null, completedSynchronously);
18    }
19
20    new public TResult EndInvoke()
21    {
22      base.EndInvoke(); // Wait until operation has completed
23      return m_result; // Return the result (if above didn't throw)
24    }
25  }

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

Code3.3

1internal sealed class LongTask
2  {
3    private Int32 m_ms; // Milliseconds;
4
5    public LongTask(Int32 seconds)
6    {
7      m_ms = seconds * 1000;
8    }
9
10    // Synchronous version of time-consuming method
11    public DateTime DoTask()
12    {
13      Thread.Sleep(m_ms); // Simulate time-consuming task
14      return DateTime.Now; // Indicate when task completed
15    }
16
17    // Asynchronous version of time-consuming method (Begin part)
18    public IAsyncResult BeginDoTask(AsyncCallback callback, Object state)
19    {
20      // Create IAsyncResult object identifying the
21      // asynchronous operation
22      AsyncResult<DateTime> ar = new AsyncResult<DateTime>(
23        callback, state);
24
25      // Use a thread pool thread to perform the operation
26      ThreadPool.QueueUserWorkItem(DoTaskHelper, ar);
27
28      return ar; // Return the IAsyncResult to the caller
29    }
30
31    // Asynchronous version of time-consuming method (End part)
32    public DateTime EndDoTask(IAsyncResult asyncResult)
33    {
34      // We know that the IAsyncResult is really an
35      // AsyncResult<DateTime> object
36      AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
37
38      // Wait for operation to complete, then return result or
39      // throw exception
40      return ar.EndInvoke();
41    }
42
43    // Asynchronous version of time-consuming method (private part
44    // to set completion result/exception)
45    private void DoTaskHelper(Object asyncResult)
46    {
47      // We know that it's really an AsyncResult<DateTime> object
48      AsyncResult<DateTime> ar = (AsyncResult<DateTime>)asyncResult;
49      try
50      {
51        // Perform the operation; if sucessful set the result
52        DateTime dt = DoTask();
53        ar.SetAsCompleted(dt, false);
54      }
55      catch (Exception e)
56      {
57        // If operation fails, set the exception
58        ar.SetAsCompleted(e, false);
59      }
60    }
61  }

來自Jeffrey Richter大師更多更詳細的異步操作方法, 請查看http://www.wintellect.com/PowerThreading.aspx,對於一些朋友可能看不懂Code3.1-3.3代碼(其實沒什麼所謂的),因為涉及到過多的線程知識,這裡出於讓你獲得更多的更深層次的(異步)認識為目的,才提供上面代碼.

下一篇章中,我們來看看微軟提供有異步調用的類是如何調用的,並從中我會給出些真實應用環境中的一些小技巧,讓你編寫的代碼更健壯更完善.

本文配套源碼

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved