【发布时间】:2022-02-28 06:13:45
【问题描述】:
我有一个IObservable<int> 序列,它在前 9 次被订阅时发出单个项目,在进一步订阅时它什么也不发出并立即完成:
int counter = 0;
IObservable<int> source = Observable.Defer(() =>
{
if (++counter < 10)
return Observable.Return(counter).Delay(TimeSpan.FromMilliseconds(100));
else
return Observable.Empty<int>();
});
现在我想重复这个序列直到它完成。所以我使用了Repeat 运算符:
source
.Repeat()
.Do(x => Console.WriteLine(x), () => Console.WriteLine("Completed"))
.Wait();
问题是这个查询永远不会完成。 Repeat 不断订阅source 序列,直到永远。更糟糕的是,当source 停止生成元素时,查询进入了一个无情的死循环,劫持了一个 CPU 核心(我的四核机器报告连续 CPU 利用率为 25%)。这是上面代码的输出:
1
2
3
4
5
6
7
8
9
我想要的是Repeat 运算符的变体,当source 停止生成元素时,它停止重复source。搜索内置的 Rx 运算符我可以看到 RepeatWhen 运算符,但显然这只能用于更快地开始下一次重复,而不是完全停止重复:
// Repeatedly resubscribes to the source observable after a normal completion and
// when the observable returned by a handler produces an arbitrary item.
public static IObservable<TSource> RepeatWhen<TSource, TSignal>(
this IObservable<TSource> source,
Func<IObservable<object>, IObservable<TSignal>> handler);
但我不是 100% 确定,因为 handler 参数的描述非常模糊,所以我可能遗漏了一些东西:
为每个观察者调用并接受可观察序列对象的函数。它应该返回一个任意项的可观察项,该可观察项应该向该任意项发出信号,以响应从源可观察项接收到完成信号。如果这个 observable 发出一个终止事件的信号,则序列会以该信号终止。
我的问题是:我怎样才能实现一个重复source 序列直到它为空的RepeatUntilEmpty 运算符?是否可以基于前面提到的RepeatWhen 操作符来实现呢?如果不是,我应该去低级(Observable.Create)并从头开始重新实现基本的Repeat 功能吗?或者我可以使用Materialize 运算符来发挥我的优势,以某种方式将它与现有的Repeat 结合起来吗?我现在没有想法。我愿意接受任何形式的解决方案,无论是高杠杆还是低杠杆。
public static IObservable<T> RepeatUntilEmpty<T>(this IObservable<T> source)
{
// What to do?
}
在我的原始代码中将Repeat 替换为RepeatUntilEmpty,应该会在发出9 元素后立即完成查询。
【问题讨论】:
-
我假设您有一个真实的情况,您提供的代码只是为了提出问题。 Rx 有
Generate运算符,可以执行您的代码尝试执行的操作:IObservable<int> source = Observable.Generate(1, x => x < 10, x => x + 1, x => x, x => TimeSpan.FromMilliseconds(100.0));。 -
@Enigmativity 老实说,激发这个问题的是这个代码:
.Publish(x => x.FirstAsync().SelectMany(c => c).Repeat()),我在this 问题中看到。重复获取已发布序列的第一个元素最终会导致源耗尽。从那时起,已发布的序列将立即发出OnCompleted未来订阅的通知。 -
@我发现带有
Publish运算符的那一行很奇怪。Publish似乎并没有做任何事情。 -
@Enigmativity 当我第一次看到这段代码时,我也很困惑。发布一个序列并逐个使用它是使用 Rx 的一种非常不寻常且惯用的方式。我不确定这是否完全错误。它可能有合法的实际应用。
-
对,是的,我明白了。它真的在推动 Rx 的时间扭曲。
标签: c# system.reactive rx.net