我想同时调用两个Web服务并在完成两个Web服务时处理响应。我使用Rx的Observable.FromAsyncPattern方法调用webservices。同时订阅多个IObservable的正确方法是什么?
我尝试过使用Zip,但它似乎并不是同时启动,只是在收到第一个结果后才开始。 修改 这是一个Zip的演示或其他一些解决我的问题的解决方案 -
class Program
{
static void Main(string[] args)
{
var observable1 = Observable.Create<int>(i =>
{
Console.WriteLine("starting 1");
System.Threading.Thread.Sleep(2000);
Console.WriteLine("done sleeping 1");
i.OnNext(1);
i.OnCompleted();
return () => { };
});
var observable2 = Observable.Create<int>(i =>
{
Console.WriteLine("starting 2");
System.Threading.Thread.Sleep(4000);
Console.WriteLine("done sleeping 2");
i.OnNext(1);
i.OnCompleted();
return () => { };
});
var m = observable1.Zip(observable2, (a, b) => new { a, b });
var n = Observable.Merge(Scheduler.ThreadPool,
observable1, observable2);
var o = Observable.When(observable1.And(observable2).Then((a, b) => new { a, b }));
m.Subscribe(
(i) => Console.WriteLine(i),
() => Console.WriteLine("finished"));
Console.Read();
}
}
结果:
starting 1
done sleeping 1
starting 2
done sleeping 2
{ a = 1, b = 1 }
finished
期望的结果:
starting 1
starting 2
done sleeping 1
done sleeping 2
{ a = 1, b = 1 }
finished
答案 0 :(得分:2)
使用Zip
扩展方法是这里的简单答案。
如果您有几个典型的异步调用(假设有单个参数):
Func<X1, IObservable<X2>> callX = Observable.FromAsyncPattern<X1, X2>(...);
Func<Y1, IObservable<Y2>> callY = Observable.FromAsyncPattern<Y1, Y2>(...);
然后你可以同时调用它们并在完成两者之后处理返回值:
callX(x1).Zip(callY(y1), (x2, y2) =>
{
...
});
答案 1 :(得分:1)
Rx Joins提供解决方案。
Observable.And
当两个可观察序列都具有可用值时匹配。
演示:
var xsL = Observable.Return(1);
var xsR = Observable.Return(2);
Observable<int> both = Observable.When(xsL.And(xsR).Then((a,b) => a + b));
答案 2 :(得分:0)
Zip是正确的答案:
Observable.Zip(someWebService(), otherWebService(), (some, other) => new { some, other })
.Subscribe(someAndOther => {
Console.WriteLine(Some is {0}, and Other is {1}", someAndOther.some, someAndOther.other);
});
假设someWebService是一个签名如下的方法:
IObservable<SomeClass> someWebService()
如果Zip没有按照您的意愿行事,那么您调用Web服务的方式就会出现问题......
此外,对于一般情况,如果您想知道一堆Observable何时全部终止,您可以使用Aggregate伪造它:
Observable.Merge(
observable1.Select(_ => Unit.Default),
observable2.Select(_ => Unit.Default),
observable3.Select(_ => Unit.Default),
observable4.Select(_ => Unit.Default))
.Aggregate(Unit.Default, (acc, _) => acc)
.Subscribe(_ => Console.WriteLine("They all finished!");
然后,如果要阻止,可以在结尾处抛出First()而不是Subscribe。
答案 3 :(得分:0)
Observable.Create不是Observable.FromAsyncPattern的正确模型; Zip,Merge和When提议的解决方案适用于Observable.FromAsyncPattern。
Observable.FromAsyncPattern添加并发性(来自底层的BeginXXX调用或使用AsyncSubject(Scheduler.ThreadPool)),而Observable.Create默认情况下将调度当前线程。
Observable.FromAsyncPattern的更好模型是:
var observable1 = Observable.Create<int>(i =>
{
return Scheduler.ThreadPool.Schedule(() =>
{
Console.WriteLine("starting 1");
System.Threading.Thread.Sleep(4000);
Console.WriteLine("done sleeping 1");
i.OnNext(1);
i.OnCompleted();
});
});
答案 4 :(得分:0)
当我在寻找别的东西时,我偶然发现了这个问题。似乎有一些混淆,为什么不能同时订阅observable2。答案很简单:默认情况下Rx不是多线程的。由您来管理线程/调度,它将有助于并发。 @Enigmativity对此不以为然,但我认为这需要更深入的解释。
具体;我们有以下(汇总)代码
var observable1 = Observable.Create<int>(i =>
{
System.Threading.Thread.Sleep(2000);
i.OnNext(1);
i.OnCompleted();
return () => { };
});
var observable2 = Observable.Create<int>(i =>
{
System.Threading.Thread.Sleep(4000);
i.OnNext(1);
i.OnCompleted();
return () => { };
});
var m = observable1.Zip(observable2, (a, b) => new { a, b });
m.Subscribe(Console.WriteLine);
如果我们逐步完成这一点,那么问题就显而易见了。
好的,这显然永远无法奏效。但幸运的是,这只是@foson用来描述他们问题的一个例子。具有讽刺意味的是,如果他们使用FromAsyncPattern,它会为他们的代码引入一些并发性,它会起作用。
使用延迟2s和4s来演示Zip的正确方法是成为并发。您可以通过在当前线程上调度OnNext或使用另一个线程/计时器来执行此操作。
var observable1 = Observable.Timer(TimeSpan.FromSeconds(2));
var observable2 = Observable.Timer(TimeSpan.FromSeconds(4));
var zipped = observable1.Zip(observable2, (a, b) => new { a, b });
zipped.Subscribe(Console.WriteLine);
这里我们使用方便的Observable.Timer工厂。它将创建一个序列,该序列在订阅时从给定时间段发布值0,然后完成。如果您对如何安排计时器有偏好,那么您也可以提供可选的计划程序,例如
var observable1 = Observable.Timer(TimeSpan.FromSeconds(2), Scheduler.ThreadPool);
var observable2 = Observable.Timer(TimeSpan.FromSeconds(4), Scheduler.TaskPool);
默认调度程序(在编写.NET 4.0库的v.1.0.10621.0时)是Scheduler.ThreadPool。
您可以在我的Rx系列简介中找到有关计划的更多信息:
http://leecampbell.blogspot.com/2010/08/reactive-extensions-for-net.html 特别是调度和线程发布
http://leecampbell.blogspot.com/2010/06/rx-part-6-scheduling-and-threading.html
我希望通过原帖来澄清问题。