2010-09-08 82 views
3

我有以下問題: 在異步情況下,我需要初始化一些自定義對象的領域之前,我可以與它的其他操作的進行,所以我做的:引發事件時,所有的異步方法調用完成

class ContainingObject 
{  
    private CustomObject _co; 

    SomeMethod() 
    { 
    _co = new CustomObject(); 
    _co.InitObjectAsyncCompleted += (s,e) => DoStuff(); 
    _co.InitObjectAsync(); 
    }  
} 

class CustomObject 
{ 
    public string Field1, Field2, Field3, Field4; 

    public EventHandler InitObjectAsyncCompleted; 

    public void InitObjectAsync() 
    { 
    }  
} 

問題在於字段也是通過對WCF服務的異步調用進行初始化的,並且在引發InitObjectAsyncCompleted事件之前,必須對所有字段進行初始化。 這裏有相當多的這些字段,每個字段都使用不同的WCF調用進行初始化,並且暗示我現在不能更改WCF部分,我看到了兩種方法來解決問題:

1)WCF鏈調用,調用初始化第一個字段,然後調用WCF初始化第二個字段,等所有字段初始化之前,然後我在最後一次WCF調用中引發「已完成」事件。

public void InitObjectAsync() 
{ 
    var proxy = new ProxyFactory.GetCustomObjectProxy; 
    proxy.GetDataForField1Completed += (s,e) => 
    { 
     Field1 = e.Result; 
     proxy.GetDataForField2Completed += (s1,e1) => 
     { 
      Field2 = e1.Result; 
      //keep this up building a chain of events, when Field4 is filled, raise 
      // InitObjectAsyncCompleted(this, null);   
     }; 
     proxy.GetDataForField2(); 
    }; 
    proxy.GetDataForField1(); 
} 

2)因爲我知道應該完成多少方法調用,在這種情況下,我可以做一個計數器。

public void InitObjectAsync() 
{ 
    int counter = 0; 
    var proxy = new ProxyFactory.GetCustomObjectProxy; 
    proxy.GetDataForField1Completed += (s,e) => 
    { 
     Field1 = e.Result; 
     if(counter >= 3) 
      InitObjectAsyncCompleted(this, null); 
     else 
      counter++; 
    }; 
    proxy.GetDataForField1(); 
    proxy.GetDataForField2Completed += (s,e) => 
    { 
     Field2 = e.Result; 
     if(counter >= 3) 
      InitObjectAsyncCompleted(this, null); 
     else 
      counter++; 
    }; 
    proxy.GetDataForField2(); 
    //repeat for all fields 
} 

我真的不喜歡任何解決方案,第一個建立事件的一個相當大的和非常可讀鏈,二是隻是...原油 - 任何人都可以提出解決這個問題的一個更優雅的方式?

回答

1

第二種方法比第一種方法更容易理解,但兩種方法都有點脆弱。

一種替代方法是跟蹤未完成的初始化請求和完成的數量,並使用此信息來決定何時觸發事件。下面是我的意思的例子:

private int _outstandingRequests = 0; 

public void InitObjectAsync() 
{ 
    RequestField(proxy.GetDataForField1, 
        proxy.GetDataForField1Completed, 
        s => Field1 = s); 

    RequestField(proxy.GetDataForField2, 
        proxy.GetDataForField2Completed, 
        s => Field2 = s); 

    RequestField(proxy.GetDataForField3, 
        proxy.GetDataForField3Completed, 
        s => Field3 = s); 
    // ... and so on... 
} 

// This method accepts two actions and a event handler reference. 
// It composes a lambda to perform the async field assignment and internally 
// manages the count of outstanding requests. When the count drops to zero, 
// all async requests are finished, and it raises the completed event. 

private void RequestField<T>(Action fieldInitAction, 
           EventHandler fieldInitCompleteEvent, 
           Action<T> fieldSetter) 
{ 
    // maintain the outstanding request count... 
    _outstandingRequests += 1; 

    // setup event handler that responds to the field initialize complete   
    fieldInitCompleteEvent += (s,e) => 
    { 
     fieldSetter(e.Result); 

     _outstandingRequests -= 1; 

     // when all outstanding requests finish, raise the completed event 
     if(_outstandingRequests == 0) 
      RaiseInitCompleted(); 
    } 

    // call the method that asynchronously retrieves the field value... 
    fieldInitAction(); 
} 

private void RaiseInitCompleted() 
{ 
    var initCompleted = InitObjectAsyncCompleted; 
    if(initCompleted != null) 
     initCompleted(this, null); 
} 
+0

感謝,透明和靈活的解決方案 – 2010-09-08 15:13:27

+0

您可能想要使用聯鎖機制來避免併發問題增量和減量操作。 Interlocked.Increment(...)是一個很好的方法來做到這一點 – 2010-09-08 21:26:06

3

如果您使用.NET 4.0的並行擴展,你可以創建多個異步任務,很容易加入他們的行列:

Task[] tasks = new Task[3] 
{ 
    Task.Factory.StartNew(() => MethodA()), 
    Task.Factory.StartNew(() => MethodB()), 
    Task.Factory.StartNew(() => MethodC()) 
}; 

//Block until all tasks complete. 
Task.WaitAll(tasks); 
+0

好辦法,如果有將是保證除了並行擴展的更相似的任務,可能會嘗試一下也 – 2010-09-08 15:14:45

+0

我喜歡這種方法,比我的好。你總是可以自己編寫相關的類。 – Lunivore 2010-09-08 15:17:19

+0

這隻有在MethodA/B/C是同步操作時纔有效。所以 這並不能解決響應完成異步操作的問題。使用任務來封裝異步方法不會執行任何操作。異步方法立即返回並且Task.WaitAll立即成功,因爲所有方法都已完成。雖然異步操作仍在後臺進行。 – 2017-02-28 12:40:03

0

把每個WCF調用在一個小包裝類。把這些類放在一個集合中(或者如果命令很重要,則列表),並且在通話結束時讓它們從集合中移除。他們還應該監視一個監視器。

Monitor.Enter。循環遍歷集合中的所有WCF調用。然後等待監視器。每次收到通知時,如果該集合不爲空,請等待。當你離開等待循環時,調用init並引發事件。如果你願意的話,你總是可以在Monitor.Wait上超時。(我經常打電話給我的鎖waitingRoom,所以很明顯發生了什麼)。

如果你將自己從WCF調用中分離出來,你就等待着,那麼這個方法很好也很容易測試,你可以做任何事情,比如記錄任何WCF調用,通過包裝類識別它失敗。

+0

正在考慮這樣的解決方案,但設置一個超時定時器的情況下,有些調用可能不會返回某種方式讓我不安使用這種方法 – 2010-09-08 15:16:57

相關問題