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 服务)

编辑:

我正在使用 3rd 方验证引擎。this.Verifier.VerifierEngine不是我写的。我只能将验证器添加到集合中。通常它们在主线程上执行以验证属性等。我需要创建一个验证器来回调服务器,因此async在 Silverlight 中。我不是手动处理线程,当我调用服务时,它会在主线程上返回,所以很好,这部分对我隐藏。我想要的只是能够“等待”直到所有验证者都完成。我现在唯一可以检查的方法就是我正在做的事情。

因此我的用户界面很古怪。完成修改字段后,用户点击“保存”,我的验证开始。我检查是否所有验证者IsVerifierRunning并给用户他需要重试的消息。我真的需要能够等待所有人完成然后继续。

  1. 我不需要担心线程,除非适当的解决方案涉及使用线程
  2. 我已经可以访问结果
4

4 回答 4

3

我使用 System.Reactive.Linq 使用 Linq 到 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);
    });

此函数使用 anAsyncSubject来确保它捕获事件触发。

现在查询变得非常简单。

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 */ });

我希望这一切都足够清楚。

于 2012-07-07T01:13:06.587 回答
1

从我听说你的要求来看,我认为一个简单的计数器就足够了。现在,我将使用与您发布的内容最接近的解决方案,并列出需要有效的假设才能使其工作。如果任何假设无效,请告诉我,我会适当地更新代码。

如果您的循环在 UI 线程上运行,则可能如下所示。这里有几个假设。首先是我们在 UI 线程上,验证的调用在 ui 线程上执行。这使 DispatcherSynchronizationContext 生效。来自 wcf 的完成事件将在它们开始的 SynchronizationContext 中执行。这将有效地序列化它们的执行,这将使您的代码具有非常单线程的行为,因此在计数器上不需要锁定/等。更多关于这里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;
        }
    }
}
于 2012-07-06T17:21:53.380 回答
0

Silverlight 确实有ManualResetEvent,它会给你你需要的东西。但是,代码需要看起来与您所拥有的不同。

是否有特定原因您不想使用计数器和自旋循环?它没有那么漂亮,但它也不需要像其他方法一样多的代码更改。

埃里克

于 2012-07-04T01:20:55.523 回答