2012-06-23 36 views
2

假設,我有IAsynchronous接口支持用於做一些操作的兩種方法( 「開始/結束」 模式):複合異步調用 - 如何?

  • IAsyncResult BeginOperation(AsyncCallback callback, object state)
  • EndOperation(IAsyncResult ar)

假設也即我有類別A : IAsynchronousB : IAsynchronous。我必須執行Compound : IAsynchronous類。 Compound的操作是調用A的操作,等待其完成,然後調用B的操作,然後照常調用回調。

的問題是如何設計Compound類:

  • 我應該保留原始state對象和Compound的操作完成時調用原回調。因此,我不能簡單地將它們傳遞給A.BeginOperationB.BeginOperation
  • 此外,Compound.EndOperation應重新拋出由A.EndOperationB.EndOperation引發的任何異常。如果A.EndOperation已引發異常,則Compound的操作不應調用B.EndOperation,因爲複合操作已失敗。
  • 如果不僅有2個內部操作,但是3個或更多,會怎樣?有一個共同的方法嗎?

要澄清,請考慮以下示例。假設,我們有Multiplier類支持下列方法:

  • IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, AsyncCallback callback, object state)
  • LargeNumber EndMultiply(IAsyncResult ar)

你想寫ThreeMultiplier類支持下列方法:

  • IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, LargeNumber z, AsyncCallback callback, object state)
  • LargeNumber EndMultiply(IAsyncResult ar)

ThreeMultiplier類應該使用Multiplier類來計算x * y * z。爲了做到這一點,它首先計算x * y(通過Multiplier.Begin/EndMultiply),然後將結果乘以z。當然,Multiplier.EndMultiply可以拋出SomeException,在任何步驟都無法計算x * y * z

什麼是最好(或好)的方式來實現這個?有什麼模式嗎?

+0

這是否與Visual Studio 2010? –

+0

@PeterRitchie最好.NET 3.5 – eigenein

回答

4

我會在編寫新代碼時避免使用APM(異步編程模型:使用IAsyncResult和Begin *和End *)。

在Visual Studio 2010中引入了任務並行庫(TPL),此時引入了任務異步模式(TAP)。此模式是支持VS 2012中新的異步/等待關鍵字的基礎框架API的基礎(C#5)。您可以使用Task.FromAsync()包裝APM實現;但如果你正在編寫新的代碼,那麼使用任務/任務將是未來的更好選擇。

使用TAP,您可以使用將異步執行委託的Task對象包裝委託。然後,您可以繼續執行第一個委託完成時將運行的其他異步任務。例如,如果您有兩個代表,其中一個需要在其他完成運行,你可以這樣做:

 Task.Factory.StartNew(() => MyMethod()) 
      .ContinueWith(() => MyOtherMethod()); 

你可以包裝在一個方法:

public void AsyncCompound(Action firstAcction, Action secondAction) 
{ 
      Task.Factory.StartNew(firstAction) 
       .ContinueWith(secondAction); 
} 

..少量定義IAsyncResult類並實現Begin和End方法的工作。對於.NET 3.5--主流模式是APM--沒有「任務」類始終可用。有一些TPL實現可能有效,但我沒有使用它們。或者,您可以查看Reactive Extensions,因爲它是實現異步操作的另一種方式 - 儘管基於事件。

APM可以得到詳細的快速,所以,我建議您儘可能使用代表。我還建議再使用一個IAsyncResult FPGA實現像一個在http://msdn.microsoft.com/en-us/magazine/cc163467.aspx例如:

public class Multiplier 
{ 
    public LargeNumber Multiply(LargeNumber x, LargeNumber y) 
    { 
     return x * y; 
    } 

    public IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, AsyncCallback callback, object state) 
    { 
     AsyncResult<LargeNumber> ar = new AsyncResult<BigInteger>(callback, state); 
     ThreadPool.QueueUserWorkItem(o => 
     { 
      var asyncResult = (AsyncResult<LargeNumber>)o; 
      try 
      { 
       var largeNumber = Multiply(x, y); 
       asyncResult.SetAsCompleted(largeNumber, false); 
      } 
      catch (Exception e) 
      { 
       asyncResult.SetAsCompleted(e, false); 
      } 
     }, ar); 
     return ar; 
    } 

    public LargeNumber EndMultiply(IAsyncResult asyncResult) 
    { 
     var ar = (AsyncResult<LargeNumber>)asyncResult; 

     return ar.EndInvoke(); 
    } 

    public IAsyncResult BeginMultiply(LargeNumber x, LargeNumber y, LargeNumber z, AsyncCallback callback, object state) 
    { 
     AsyncResult<LargeNumber> ar = new AsyncResult<LargeNumber>(callback, state); 

     BeginMultiply(x, y, (asyncResult1) => 
     { 
      var firstResult = EndMultiply(asyncResult1); 
      BeginMultiply(firstResult, z, (asyncResult2) => 
      { 
       var secondResult = EndMultiply(asyncResult2); 
       ar.SetAsCompleted(secondResult, true); 
      }, state); 
     }, state); 
     return ar; 
    } 
} 

然後可以按如下方式使用異步計算的值,並返回到當前線程:

var asyncResult = multiplier.BeginMultiply(x, y, z, ar => { }, null); 
var result = multiplier.EndMultiply(asyncResult); 

或者,你可以鏈切換到其他代碼在後臺線程中執行:

 multiplier.BeginMultiply(x, y, z, ar => 
              { 
              var result = multiplier.EndMultiply(ar); 
              /* TODO: something with result on this background thread */ 
              }, null); 

...這將是由你來決定你怎麼會GE這導致它需要去的地方,以及如何與名爲BeginMultiply的線程進行交互。