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私が書いたものではありません。コレクションに追加できるのはベリファイアのみです。async通常、それらはプロパティなどを検証するためにメイン スレッドで実行されます。私の必要性は、Silverlight でサーバーにコールバックするベリファイアを作成することです。私はスレッドを手動で扱っていません。サービスを呼び出すと、メインスレッドに返されるので問題ありません。この部分は私から隠されています。私が望むのは、すべてのベリファイアが完了するまで「待機」できることだけです。今確認できる唯一の方法は、自分のやり方です。

したがって、私の UI は風変わりです。フィールドの変更が完了した後にユーザーが保存すると、検証が開始されます。すべての検証者を確認IsVerifierRunningし、再試行する必要があることをユーザーに伝えます。すべてが完了するのを待ってから続行できるようにする必要があります。

  1. 適切な解決策にスレッドの操作が含まれない限り、スレッドについて心配する必要はありません
  2. すでに結果にアクセスできます
4

4 に答える 4

3

System.Reactive.Linq を使用して Linq to Rx を使用します。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;
    }
于 2012-07-04T02:17:22.393 に答える
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>(0 個以上の値を返すオブザーバブル) をIObservable<T[]>(0 個以上の値の単一の配列を返すオブザーバブル) に変更します。言い換えれば、これはすべてのベリファイアが終了するまで待ってから、それらをすべて返します。

これで、クエリをサブスクライブし、必要なコードを実行して、すべてのベリファイアが完了したことを確認するだけです。

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

ベリファイアが終了するたびに何らかのコードを実行したい場合、およびそれらすべてが終了たときに、次のように呼び出しを外して.ToArray()クエリにサブスクライブできます。

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

これがすべて十分に明確であることを願っています。

于 2012-07-07T01:13:06.587 に答える
1

あなたの要件について聞いたところによると、単純なカウンターで十分だと思います。今のところ、私はあなたが投稿したものに最も近いソリューションを使用し、それが機能するために有効である必要がある仮定をリストします。前提条件が無効な場合はお知らせください。コードを適切に更新します。

ループがUIスレッドで実行されている場合は、次のようになります。ここにはいくつかの仮定があります。1つは、UIスレッドを使用していて、検証の呼び出しがUIスレッドで実行されていることです。これにより、DispatcherSynchronizationContextが有効になります。wcfからの完了イベントは、開始したときと同じSynchronizationContextで実行されます。これにより、実行が効果的にシリアル化され、コードに非常にシングルスレッドの動作が与えられるため、カウンターでロックなどを行う必要はありません。詳細については、 http://msdn.microsoft.com/en-us/library/z8chs7ft( v = vs.95 ).aspxを参照してください。2番目の前提は、検証呼び出しは、エラーが発生した場合でも常に完了するということです。

登録解除の要件に一致するように編集されました。これにはメモ帳を使用したので、タイプミスに耐えてください。イベント引数によっては、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;
        }
    }
}
于 2012-07-06T17:21:53.380 に答える
0

SilverlightにはManualResetEventがあり、必要なものを提供します。ただし、コードは現在のものとは異なって見える必要があります。

カウンターやスピンループを使いたくないという特別な理由はありますか?それほどきれいではありませんが、他のアプローチほど多くのコード変更を必要としません。

エリック

于 2012-07-04T01:20:55.523 に答える