2008-11-18 150 views
3

我正在爲客戶端/服務器應用程序中的「客戶端」類實現異步命令模式。我在過去做過一些套接字編碼,我喜歡它們在Socket/SocketAsyncEventArgs類中使用的新異步模式。異步命令模式 - 異常處理

我的異步方法如下所示:public bool ExecuteAsync(Command cmd);如果執行處於掛起狀態,則返回true;如果同步完成,則返回false。 我的問題是:即使發生異常,我是否應該始終調用回調函數(cmd.OnCompleted)?還是應該從ExecuteAsync引發異常?

這裏有一些更多的細節,如果你需要它們。這與使用SocketAsyncEventArgs類似,但我們的類名爲SomeCmd,而不是SocketAsyncEventArgs。

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!"); 
cmd.OnCompleted += this.SomeCmd_OnCompleted; 
this.ConnectionToServer.ExecuteAsync(cmd); 

與Socket類,如果你需要與你的回調方法(在這種情況下SomeCmd_OnCompleted)協調,則可以使用ExecuteAsync的返回值知道操作是否掛起(真),或者如果操作同步完成。

SomeCmd cmd = new SomeCmd(23, 14, 10, "hike!"); 
cmd.OnCompleted += this.SomeCmd_OnCompleted; 
if(this.ConnectionToServer.ExecuteAsync(cmd)) 
{ 
    Monitor.Wait(this.WillBePulsedBy_SomeCmd_OnCompleted); 
} 

這裏是我的基類的極大簡化版本,但你可以看到它是如何工作的:

class Connection 
{ 
    public bool ExecuteAsync(Command cmd) 
    { 
     /// CONSIDER: If you don't catch every exception here 
     /// then every caller of this method must have 2 sets of 
       /// exception handling: 
     /// One in the handler of Command.OnCompleted and one where ExecuteAsync 
     /// is called. 
     try 
     { 
     /// Some possible exceptions here: 
     /// 1) remote is disposed. happens when the other side disconnects (WCF). 
     /// 2) I do something wrong in TrackCommand (a bug that I want to fix!) 
      this.TrackCommand(cmd); 
      remote.ServerExecuteAsync(cmd.GetRequest()); 
      return true; 
     } 
     catch(Exception ex) 
     { 
      /// Command completing synchronously. 
      cmd.Completed(ex, true); 
      return false; 
     } 
    } 
    /// <summary>This is what gets called by some magic when the server returns a response.</summary> 
    internal CommandExecuteReturn(CommandResponse response) 
    { 
     Command cmd = this.GetTrackedCommand(response.RequestId); 
     /// Command completing asynchronously. 
     cmd.Completed(response, false); 
    } 

    private IServer remote; 
} 

abstract class Command: EventArgs 
{ 
    internal void Completed(Exception ex, bool synchronously) 
    { 
     this.Exception = ex; 

     this.CompletedSynchronously = synchronously; 

     if(this.OnCompleted != null) 
     { 
      this.OnCompleted(this); 
     } 
    } 

    internal void Completed(CommandResponse response, bool synchronously) 
    { 
     this.Response = response; 
     this.Completed(response.ExceptionFromServer, synchronously) 
    } 

    public bool CompletedSynchronously{ get; private set; } 

    public event EventHandler<Command> OnCompleted; 

    public Exception Exception{ get; private set; } 

    internal protected abstract CommandRequest GetRequest(); 
} 

回答

3

從調度點拋出異常可能會或可能不會有用

調用回調傳遞一個異常參數要求完成回調做2件不同的事情

第二回調異常報告可能是有意義而不是

+0

現在,爲什麼我沒有想到這一點嗎?有趣的是,我之前已經爲一個運行一系列查詢的類實現了這個功能 – 2008-11-18 03:15:02

1

我會拋出一個自定義異常,而不是調用完成回調。畢竟,如果發生異常,命令沒有完成。

4

我會不是在ExecuteAsync中引發異常,而是設置回調的異常條件。這將爲異步邏輯創建一致的編程方式,並減少重複代碼。客戶可以調用這個類並期待一種處理異常的方法。這將提供更少的bug,更脆弱的代碼。

0

在一個地方處理異常要容易得多。我將使用以下區別:對於應該處理的異常,將它們放在回調中。它使得使用該類更簡單。對於不應捕獲的異常(例如,ArgumentException),請引入ExecuteAsync。我們希望未處理的異常儘快炸燬。

5

在.NET(異步操作,至少在BackgroundWorkerBeginInvoke()/EndInvoke()方法對一個一般的模式是有用於分隔實際的返回值或發生的任何異常回調結果對象。這是的責任回調來處理異常

有些類似C#的僞代碼:

 

private delegate int CommandDelegate(string number); 

private void ExecuteCommandAsync() 
{ 
    CommandDelegate del = new CommandDelegate(BeginExecuteCommand); 
    del.BeginInvoke("four", new AsyncCallback(EndExecuteCommand), null); 
} 

private int BeginExecuteCommand(string number) 
{ 
    if (number == "five") 
    { 
     return 5; 
    } 
    else 
    { 
     throw new InvalidOperationException("I only understand the number five!"); 
    } 
} 

private void EndExecuteCommand(IAsyncResult result) 
{ 
    CommandDelegate del; 
    int retVal; 

    del = (CommandDelegate)((AsyncResult)result).AsyncDelegate; 

    try 
    { 
     // Here's where we get the return value 
     retVal = del.EndInvoke(result); 
    } 
    catch (InvalidOperationException e) 
    { 
     // See, we had EndExecuteCommand called, but the exception 
     // from the Begin method got tossed here 
    } 
} 
 

所以,如果你ExecuteCommandAsync(),它立即返回BeginExecuteCommand()在一個單獨的線程啓動。如果它拋出一個異常,那麼除非你在IAsyncResult上調用EndInvoke()(你可以投到AsyncResult,這是有記錄的,但如果演員讓你不舒服,你可以在狀態中傳遞它)。異常處理代碼是「自然地安置」,圍繞在那裏你將與方法的返回值進行交互。

欲瞭解更多信息,結賬更多信息the IAsyncResult pattern on MSDN

希望這有助於。

+0

請注意,即使在.NET Framework代碼中,也只能在EndXYZ上拋出異常。例如,在指向不存在的端點的WCF客戶端服務代理上調用BeginXYZ。異常立即拋出。 – 2009-05-29 11:24:07