2012-07-04 30 views
3

在Silverlight應用程序,我需要並行運行多個異步調用 - 他們將調用服務器來檢查數據等我的僞代碼的設置是這樣如何正確連接多個異步方法的結果?

public interface IAsyncVerifier 
{ 
    event EventHandler OnCompleted; 

    void Verify(); 
} 

// Subscribe to verifier completion events 
foreach (var verifier in this.VerifierEngine.GetVerifiers()) 
{ 
    var asyncVerifier = verifier as IAsyncVerifier; 
    if (asyncVerifier == null || !asyncVerifier.IsVerifierRunning()) continue; 

    asyncVerifier.OnCompleted += (s, a) => DoWhat(); // ???????? 
    asyncVerifier.Verify(); 
} 

所以,在我的心目中,我有共同的接口爲所有這些任務。然後在執行之前,我訂閱他們所有的事件和...?

我需要以某種方式等待他們完成並繼續我在做什麼。如何正確編寫剩餘的代碼?我看到某種類型的counter,每當我撥打Verify()時我都會增加,並且每次獲得OnCompleted時遞減。但是我沒有全面瞭解如何將它們包裝在一起,所以沒有味道。此外,Silverlight中運行那些在後臺線程,如果我理解正確的話(調用WCF服務)

編輯:

我與第三方驗證發動機的工作。 this.Verifier.VerifierEngine不是我寫的。我只能將驗證者添加到收藏中。通常它們在主線程上執行以驗證屬性等。我的需求是創建可以回撥給服務器的驗證程序,因此在Silverlight中爲async。我沒有手動處理線程,當我調用服務時,它返回主線程,所以沒關係,這部分對我來說是隱藏的。我想要的只是能夠「等待」,直到所有驗證者完成。我現在唯一可以檢查的方式就是我的方式。

因此,我的UI很古怪。在完成修改字段後,用戶點擊'保存並開始驗證。我檢查是否所有驗證者IsVerifierRunning並給用戶留言說他需要重試。我真的需要能夠等待所有完成,然後繼續。

  1. 我不需要擔心線程,除非妥善解決將涉及使用線程
  2. 我有機會獲得結果已經
+0

你可以添加更多的細節?調用驗證已經是非阻塞的?或者你需要讓他們不被阻塞?你需要訪問驗證結果嗎?或者只是在所有驗證完成時通知/繼續? –

+0

另外,你的驗證循環已經離開UI線程嗎?如果你想使用任何類型的阻塞同步(比如其他應答者指出的手動重置事件),你需要確保你不在UI線程上。 –

+0

你的意思是像這個例子一樣傳達嗎?「2個異步調用,當兩個調用都完成時,你想繼續進行」 – vaibhav

回答

2

我也建議使用Reactive Framework(Rx)來做你想做的事。

首先,您需要一個函數將IAsyncVerifier轉換爲可觀察的函數,以啓動驗證過程並訂閱OnCompleted。那就是:

Func<IAsyncVerifier, IObservable<Unit>> startVerifier = av => 
    Observable.Create<Unit>(o => 
    { 
     var subject = new AsyncSubject<Unit>(); 
     var s1 = 
      Observable 
       .FromEventPattern(
        h => av.OnCompleted += h, 
        h => av.OnCompleted -= h) 
       .Select(_ => Unit.Default) 
       .Subscribe(subject); 
     var s2 = subject.Subscribe(o); 
     av.Verify(); 
     return new CompositeDisposable(s1, s2); 
    }); 

,此函數使用AsyncSubject,以確保它捕獲的事件觸發。

現在查詢變得非常簡單。

var query = (
    from av in VerifierEngine 
     .GetVerifiers() 
     .OfType<IAsyncVerifier>() 
     .ToObservable() 
    where !av.IsVerifierRunning() 
    from result in startVerifier(av) 
    select av) 
     .ToArray(); 

在RX世界,因爲這是一個SelectMany查詢默認是線程池運行這些 - 所以它是並行運行。

最終的.ToArray()調用與該函數的可枚舉版本稍有不同。可觀察的版本將IObservable<T>(返回零個或多個值的可觀察值)更改爲IObservable<T[]>(可返回包含零個或多個值的單個數組的觀察值)。換句話說,這將一直等到所有的驗證者完成之後才返回全部。

現在,您只需訂閱查詢並運行您想要知道所有驗證者已完成的任何代碼。

query.Subscribe(avs => DoWhat()); 

如果您希望在每次檢驗完成時間運行一些代碼時,他們都完成,那麼你可以脫掉.ToArray()呼叫,訂閱查詢,像這樣:

query.Subscribe(av => { /* Each */ },() => { /* All */ }); 

我希望這足夠清楚。

+0

是Rx的一部分SL 5或我需要打包它?我真的很想學rx,現在它就像是一個魔法,我也曾經多次看過它。很難把我的頭包裹起來。我不得不承認代碼看起來非常尖銳... – katit

+0

@katit - 我不確定Rx的哪個版本在SL5中 - 或者它在那裏,但是DLL很小,所以它不應該是一個太大的負擔。是的,你應該學習Rx - 這真是太棒了,它會幫助你做很多異步操作(就像你說的那樣)。 :-) – Enigmativity

0

的Silverlight確實有ManualResetEvent,這會給你什麼你需要。但是,代碼需要看起來與您擁有的不同。

是否有一個特別的原因,你不想使用計數器和旋轉循環?它不是很漂亮,但它也不會像其他方法那樣需要儘可能多的代碼更改。

埃裏克

+0

我該如何做旋轉循環?這可能是醜陋的,但會爲我所需要的工作和可靠工作。有問題的代碼只是一個原型,所以我可以重構,因爲我需要...上次我做旋轉循環像6年前與VB6 DoEvents():)我不認爲silverlight具有DoEvents功能。 – katit

+0

如果你有一個計數器,每個工作項增加,然後有一個像while(計數器> 0)Thread.Sleep(500) –

+0

聲明但它需要在後臺線程? – katit

3

我使用Linq於Rx 使用System.Reactive.Linq; Observable.FromEvent 郵編方法

var InitNetCellCache = InitNetCell(); 
      InitNetCellCache.Subscribe((s) => 
      { 
       Debug.WriteLine("init NetCell Cache OK."); 

       //ScheduleCrrentThread(innerAction); 
      }); 
      var InitKPIMapCache = InitKPIMapCell(); 
      var zipped = InitKPIMapCache.Zip(InitNetCellCache, (left, right) => left + " - " + right); 

      zipped.Subscribe((s) => 
       { 
        //When all async methods are completed 
        runtime.Show(); 
       } 
      ); 

    IObservable<object> InitNetCell() 
    { 
     IUnityContainer unityContainer = ServiceLocator.Current.GetInstance<IUnityContainer>(); 
     INetCellManager inc = unityContainer.Resolve<INetCellManager>(); 
     var NetCell = Observable.FromEvent<InitCacheCompletedDelegate, object>(
     h => ((object sendera) => h.Invoke(sendera)), 
     h => inc.InitCacheCompleted += h, 
     h => inc.InitCacheCompleted -= h); 
     //Run you async method 
     inc.InitCache(); 
     return from e in NetCell select e; 
    } 
+0

http://msdn.microsoft.com/en-us/library/system.reactive.linq(v=vs.103).aspx –

1

從我聽說過你的要求,我想一個簡單的計數器就足夠了。現在,我將使用與您發佈的內容最接近的解決方案,並列出爲使其正常工作而需要有效的假設。如果有任何假設無效,請告訴我,我會適當更新代碼。

如果您的循環在UI線程上運行,它可以如下所示。這裏有幾個假設。首先是我們在UI線程和調用來驗證ui線程上的執行。這使DispatcherSynchronizationContext生效。來自wcf的完成事件將在它們開始的同一個SynchronizationContext中執行。這將有效地將其執行序列化,這將使您的代碼具有非常單一的線程行爲,因此在計數器上不需要鎖定/ etc。還有一些在這裏http://msdn.microsoft.com/en-us/library/z8chs7ft(v=vs.95).aspx。第二個假設是驗證調用將始終完成,即使它們錯誤。

編輯以匹配取消訂閱要求。忍受任何錯字,因爲我只是用記事本。根據您的事件參數,EventHandler可能必須是特定類型的事件處理程序。

int activeVerifiers = 0; 
List<VerificationCleanup> cleanups = new List<VerificationCleanup>(); 

EventHandler completionHandler = (s, a) => 
{ 
    activeVerifiers--; 
    if(activeVerifiers == 0) 
    { 
      //execute some sort of message/notification/all complete event 

      foreach(VerificationCleanup cleanup in cleanups) 
      { 
      cleanup.Cleanup(); 
      } 
    } 
}; 

foreach (var verifier in this.VerifierEngine.GetVerifiers()) 
{ 
    var asyncVerifier = verifier as IAsyncVerifier; 
    if (asyncVerifier == null || !asyncVerifier.IsVerifierRunning()) continue; 

    cleanups.Add(new VerificationCleanup(completionHandler, asyncVerifier)); 

    activeVerifiers++; 
    asyncVerifier.OnCompleted += completionHandler 
    asyncVerifier.Verify(); 
} 

private class VerificationCleanup 
{ 
    private EventHandler eventHandler = null; 
    private IAsyncVerifier verifier = null; 

    public VerificationCleanup(EventHandler eventHandler, IAsyncVerifier verifier) 
    { 
     this.eventHandler = eventHandler; 
     this.verifier = verifier; 
    } 

    public void Cleanup() 
    { 
     if(this.verifier != null) 
     { 
      this.verifier.OnCompleted -= this.eventHandler; 
     } 
    } 
} 
+0

所有的假設都是正確的,它幾乎與一個「但」一起工作。應該有一些代碼來取消訂閱事件。我怎麼做?現在與匿名代表,如果我把debug.trace裏面我看到每次例程調用我得到的OnCompleted數deld – katit

+0

編輯來處理un wireup –

相關問題