在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
时递减。但我没有全面了解如何将它包装在一起,所以它没有味道。此外,如果我理解正确(调用WCF服务)
编辑:
我正在使用第三方验证引擎。 this.Verifier.VerifierEngine
不是我写的。我只能添加验证器到集合。通常它们在主线程上执行以验证属性等。我需要创建验证程序,使得回调服务器,因此在Silverlight中async
。我不是手动处理线程,当我调用服务它返回主线程所以它很好,这部分隐藏我。我想要的就是能够“等待”直到所有验证者完成。我现在唯一能检查的方法就是我正在做的事情。
因此我的用户界面很古怪。完成修改后,用户点击“保存”并开始验证。我检查是否所有验证者IsVerifierRunning
并向用户提供他需要重试的消息。我真的需要等待所有人完成然后继续。
答案 0 :(得分: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;
}
答案 1 :(得分:2)
我也建议使用Reactive Framework(Rx)来做你想做的事。
首先,你需要一个能够将IAsyncVerifier
变成一个可以启动验证过程并订阅OnCompleted
的observable的函数。这是:
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[]>
(一个可返回零个或多个值的单个数组的observable)。换句话说,这将等到所有验证器完成后再返回所有验证器。
现在您只需订阅查询并运行您想知道所有验证者已完成的任何代码。
query.Subscribe(avs => DoWhat());
如果你想在每次验证器完成和时运行一些代码,那么你可以取消.ToArray()
调用并订阅查询,如下所示:
query.Subscribe(av => { /* Each */ }, () => { /* All */ });
我希望这一切都很清楚。
答案 2 :(得分: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;
}
}
}
答案 3 :(得分:0)
Silverlight确实拥有ManualResetEvent,可以满足您的需求。但是,代码需要与您拥有的代码不同。
您是否有特殊原因想要使用计数器和旋转循环?它不是那么漂亮,但它也不需要像其他方法那样多的代码更改。
埃里克