C# 观察其他观察者未观察到的值
我有一个能发出独特值的可观察对象,例如C# 观察其他观察者未观察到的值,c#,system.reactive,C#,System.reactive,我有一个能发出独特值的可观察对象,例如 var source=Observable.Range(1100.Publish(); source.Connect(); 我希望从两个观察者处观察其值,但每个观察者仅收到其他观察者未看到的值的通知 因此,如果第一个观察者包含值10,则第二个观察者永远不会收到10值的通知 更新 我选择了阿斯蒂的答案,因为这是第一个答案,尽管它的方向是正确的,但还是投票支持了什洛莫的答案。太糟糕了,我不能同时接受这两个答案,因为@Shlomo answer更为正确,我真的
var source=Observable.Range(1100.Publish();
source.Connect();
我希望从两个观察者处观察其值,但每个观察者仅收到其他观察者未看到的值的通知
因此,如果第一个观察者包含值10,则第二个观察者永远不会收到10值的通知
更新
我选择了阿斯蒂的答案,因为这是第一个答案,尽管它的方向是正确的,但还是投票支持了什洛莫的答案。太糟糕了,我不能同时接受这两个答案,因为@Shlomo answer更为正确,我真的很感谢他在这个标签上的帮助。对于不同的观察者,观察者的行为不应该有所不同;更好的方法是为每个观察者提供自己的过滤观察值 这就是说,如果您的约束要求您在单个可观察对象中需要此行为,那么我们可以使用循环方法
public static IEnumerable<T> Repeat<T>(this IEnumerable<T> source)
{
for (; ; )
foreach (var item in source.ToArray())
yield return item;
}
public static IObservable<T> RoundRobin<T>(this IObservable<T> source)
{
var subscribers = new List<IObserver<T>>();
var shared = source
.Zip(subscribers.Repeat(), (value, observer) => (value, observer))
.Publish()
.RefCount();
return Observable.Create<T>(observer =>
{
subscribers.Add(observer);
var subscription =
shared
.Where(pair => pair.observer == observer)
.Select(pair => pair.value)
.Subscribe(observer);
var dispose = Disposable.Create(() => subscribers.Remove(observer));
return new CompositeDisposable(subscription, dispose);
});
}
结果:
One sees 1
Two sees 2
One sees 3
Two sees 4
One sees 5
Two sees 6
One sees 7
Two sees 8
One sees 9
Two sees 10
如果你已经有了一个观察者列表,代码就会简单得多。编辑:@Asti修复了他的错误,我根据他的答案修复了我的错误。现在,我们的答案基本相同。我有一个想法,如何做一个纯粹的反应,如果我有时间,我会张贴后 固定代码:
public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
subscribers.Subscribe(l => latest = l);
var shared = source
.Select((v, i) => (v, i))
.WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
.Publish()
.RefCount();
return Observable.Create<T>(observer =>
{
subscribers.OnNext(latest.Add(observer));
var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));
var sub = shared
.Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
.Select(t => t.v)
.Subscribe(observer);
return new CompositeDisposable(dispose, sub);
});
}
这并不是:
var source = Observable.Range(1, 20).Publish();
var dist = source.RoundRobin();
dist.Take(1).Subscribe(i => Console.WriteLine($"One sees {i}"));
dist.Subscribe(i => Console.WriteLine($"Two sees {i}"));
输出为:
One sees 1
Two sees 1
Two sees 2
Two sees 3
Two sees 4
...
我起初以为是虫子,但现在我不确定。Repeat
中的.ToArray()
应该会处理这个问题。我还编写了一个纯可观察的实现,它有相同的bug。此实现不能保证完美的循环,但这不是问题:
public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
subscribers.Subscribe(l => latest = l);
var shared = source
.Select((v, i) => (v, i))
.WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
.Publish()
.RefCount();
return Observable.Create<T>(observer =>
{
subscribers.OnNext(latest.Add(observer));
var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));
var sub = shared
.Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
.Select(t => t.v)
.Subscribe(observer);
return new CompositeDisposable(dispose, sub);
});
}
公共静态IObservable RoundRobin2(此IObservable源)
{
var subscribers=newbehaviorsubject(ImmutableList.Empty);
ImmutableList latest=ImmutableList.Empty;
订阅(l=>latest=l);
var共享=源
.选择((v,i)=>(v,i))
.最晚从(订户,(t,s)=>(t.v,t.i,s))
.Publish()
.RefCount();
返回可观察的。创建(观察者=>
{
subscribers.OnNext(latest.Add(observer));
var dispose=Disposable.Create(()=>subscribers.OnNext(latest.Remove(observer));
var sub=共享
其中(t=>t.i%t.s.Count==t.s.FindIndex(o=>o==observer))
.选择(t=>t.v)
.签署(观察员);
返回新的CompositeDisposable(dispose,sub);
});
}
这是一个使用TPL数据流的简单分布式队列实现。但对于不同的观察者看不到相同的价值观,其行为不正确的可能性很小。这不是循环赛,但实际上有背压语义
public static IObservable<T> Distribute<T>(this IObservable<T> source)
{
var buffer = new BufferBlock<T>();
source.Subscribe(buffer.AsObserver());
return Observable.Create<T>(observer =>
buffer.LinkTo(new ActionBlock<T>(observer.OnNext, new ExecutionDataflowBlockOptions { BoundedCapacity = 1 })
);
}
我可能更喜欢完全跳过Rx,只使用TPL数据流。回答得好!请注意:
Repeat
扩展方法包含在包中。谢谢!我先写了这个,然后意识到他可能会说没有叫做Repeat
的方法,所以我写了这个。以前发生过:DAlso,EnumerableEx
中的Repeat
的行为略有不同。我使用ToArray()
强制eval,否则列表可能会出现“集合在枚举时被修改”错误。是的,如果在source.Connect()之后添加了另一个订阅服务器,这将是一个问题。或者,您可以将ToArray
和Repeat
:subscribers.ToArray().Repeat()
。谢谢Theodor!我也从你的答案中学到了很多东西!:)伟大的答案是@Shlomo和@Asti,关于完美循环
的一个小小的澄清是最受欢迎的,因为循环技术依赖于一个简单的mod操作,用户数量可以随时改变,不能保证观察者不会错过机会。最终没有那么大的问题。一个项目不会被遗漏,但是一个观察者可能会被跳过,根据你的问题,这不是一个大问题。我认为这可能是通过Rx中的某种窗口来实现的,但我可以看到性能很差。我认为最好的表现方式是在TPL数据流中,而不是Rx。如果你有多余的投票权,你也可以对这个问题进行投票。这是一个好主意!:-)在Observable.Range(1100)
上调用.Publish()
不是一个好主意,否则无论以后有多少订阅者,它都只运行一次。谢谢,我只是想表达一个热的Observable,而不是一个在每个订阅上发出所有信息的函数。我想更好的办法是在发射之间增加一些延迟。
public static IObservable<T> RoundRobin2<T>(this IObservable<T> source)
{
var subscribers = new BehaviorSubject<ImmutableList<IObserver<T>>>(ImmutableList<IObserver<T>>.Empty);
ImmutableList<IObserver<T>> latest = ImmutableList<IObserver<T>>.Empty;
subscribers.Subscribe(l => latest = l);
var shared = source
.Select((v, i) => (v, i))
.WithLatestFrom(subscribers, (t, s) => (t.v, t.i, s))
.Publish()
.RefCount();
return Observable.Create<T>(observer =>
{
subscribers.OnNext(latest.Add(observer));
var dispose = Disposable.Create(() => subscribers.OnNext(latest.Remove(observer)));
var sub = shared
.Where(t => t.i % t.s.Count == t.s.FindIndex(o => o == observer))
.Select(t => t.v)
.Subscribe(observer);
return new CompositeDisposable(dispose, sub);
});
}
public static IObservable<T> Distribute<T>(this IObservable<T> source)
{
var buffer = new BufferBlock<T>();
source.Subscribe(buffer.AsObserver());
return Observable.Create<T>(observer =>
buffer.LinkTo(new ActionBlock<T>(observer.OnNext, new ExecutionDataflowBlockOptions { BoundedCapacity = 1 })
);
}
One sees 1
Two sees 2
One sees 3
Two sees 4
One sees 5
One sees 6
One sees 7
One sees 8
One sees 9
One sees 10