我想做的是:
DoWork
)作为其工作的一部分,将通过多个Worker
类订阅多个热门输入DoWork
订阅的所有更新DoWork
完成之前,至少有一个传入的事件可能会被触发。问题:
Subject
这是正确的方法吗?感觉应该有更好的方式吗?如何确保在处置Main
中的订阅后,所有incomingX
订阅也会被处理 - 即Main
应该控制所有订阅的生命周期。
void Main()
{
var worker = new Worker();
using (worker.UpdateEvents.Subscribe(x => Console.WriteLine()))
{
worker.DoWork();
}
}
public class Worker1
{
private readonly Subject<string> updateEvents = new Subject<string>();
public IObservable<string> UpdateEvents { get { return updateEvents; } }
public void DoWork()
{
// Do some work
// subscribe to a hot observable (events coming in over the network)
incoming1.Subscribe(updateEvents);
var worker2 = new Worker2();
worker2.UpdateEvents.Subscribe(updateEvents);
worker2.DoWork();
}
}
public class Worker2
{
private readonly Subject<string> updateEvents = new Subject<string>();
public IObservable<string> UpdateEvents { get { return updateEvents; } }
public void DoWork()
{
// Do some work
// subscribe to some more events
incoming2.Subscribe(updateEvents);
var workerN = new WorkerN();
workerN.UpdateEvents.Subscribe(updateEvents);
workerN.DoWork();
}
}
答案 0 :(得分:3)
詹姆斯的回答(使用Subject
和Merge
)捕捉了问题的本质。这个答案提供了一种我认为在这种情况下有用的模式(基于你对詹姆斯答案的评论)。
基本上,模式是让您的工作人员在调用IObservable
之前公开调用者将订阅的DoWork
。但是这种API(在调用B之前调用A)是有问题的,因为它引入了时间耦合。
为了消除时间耦合,你最终将你的工作者本身变成一个冷的Observable ,当调用者订阅时隐式调用DoWork
。一旦你意识到冷可观察力的强大功能以及在观察者订阅时使用Observable.Create
采取行动的能力,天空是你可以创建的Rx链的限制,而不需要达到Subject
。以下是基于原始代码的示例。
Worker
很简单。它只是订阅了incoming1
和Worker2
。
Worker2
稍微复杂一些。它订阅incoming2
,执行一些额外的工作,然后最终订阅WorkerN
。
始终保持原始代码示例无法执行的正确OnError
,OnCompleted
逻辑。这意味着Main
看到的可观察流不会Complete
直到所有的传入流和工作流完成。但是,只要任何传入的流或工作流失败,Main
就会失败。只要有Subscribe(someSubject)
个流完成,您的代码示例会多次调用Subject
,导致incoming
完成(因此Main的传入流完成)。
public class Worker1
{
public IObservable<string> UpdateEvents { get; private set; };
public Worker1()
{
// Each time someone subscribes, create a new worker2 and subscribe to the hot events as well as whatever worker2 produces.
UpdateEvents = Observable.Create(observer =>
{
var worker2 = new Worker2();
return incoming1.Merge(worker2.UpdateEvents).Subscribe(observer);
});
}
}
public class Worker2
{
public IObservable<string> UpdateEvents { get; private set; };
public Worker2()
{
// Each time someone subscribes, create a new worker and subscribe to the hot events as well as whatever worker2 produces.
UpdateEvents = Observable.Create(observer =>
{
// maybe this version needs to do some stuff after it has subscribed to incoming2 but before it subscribes to workerN:
var doWorkThenSubscribeToWorker = Observable.Create(o =>
{
DoWork(o);
var worker = new WorkerN();
return worker.UpdateEvents.Subscribe(o);
}
return incoming2.Merge(doWorkThenSubscribeToWorker).Subscribe(observer);
});
}
private void DoWork(IObserver<string> observer)
{
// do some work
observer.OnNext("result of work");
}
}
void Main()
{
var worker = new Worker();
worker.UpdateEvents.Do(x => Console.WriteLine()).Wait();
}
答案 1 :(得分:1)
很难完全按照您的要求进行操作 - 我认为small but complete program会有所帮助。
也就是说,使用Subject
将输入引入Rx管道没有任何问题 - 在StackOverflow和其他地方有很多关于它的文章,所以我赢了重新开始吧。
仅仅按照你问题的标题,我想知道以下是否适合你的目的?
为此,您可以在流媒体流上使用Merge
。您的流必须都是相同类型 - 如果不是,您可以创建合适的容器类型并使用Select
将它们投影到该类型中。为简单起见,我假设统一类型为long
。
开始为流创建容器:
var container = new Subject<IObservable<long>>();
然后合并包含的流:
var combined = container.Merge();
订阅合并以通常方式使用结果,并将订阅部署为一次取消订阅所有流。
然后您可以按照以下方式添加流:
// assume we got this from somewhere - e.g. a "worker" factory function
// Observable.Create may well be helpful to create an observable
// that initiates getting data from a network connection upon its subscription
IObservable<long> someNewStream;
// add the someNewStream to the container (it will be subscribed to once added)
container.OnNext(someNewStream);
// dump out the combined results to the console,
// IRL you would subscribe to this to process the results
var subscription = combined.Subscribe(Console.WriteLine);
// add a stream of longs
container.OnNext(Observable.Interval(TimeSpan.FromSeconds(1)));
Console.WriteLine("Stream 1 added");
Console.ReadLine();
// add another stream
container.OnNext(Observable.Interval(TimeSpan.FromSeconds(1)));
Console.WriteLine("Step 2");
Console.ReadLine();
// this will unsubscribe from all the live streams
subscription.Dispose();