阅读explanation后的原因
Observable.Return(5)
.Repeat()
.Take(1)
永远不会完成,但是
Observable.Return(5, Scheduler.CurrentThread)
.Repeat()
.Take(1)
按预期工作。我仍然感到困惑,我无法告诉为什么currentThread
实际上解决了这个问题。有人可以给出明确的解释吗?
答案 0 :(得分:6)
Ned Stoyanov在上述评论中提供的link得到了Dave Sexton的精彩解释。
我会尝试用不同的方式来说明它。举个例子,在RecursiveTest方法中发生递归调用。
public class RecursiveTest()
{
private bool _isDone;
public void RecursiveMethod()
{
if (!_isDone)
{
RecursiveMethod();
// Never gets here...
_isDone = true;
}
}
}
您可以很容易地看到这将无限递归(直到StackOverflowException),因为_isDone永远不会被设置为true。这是一个过于简化的例子,但它基本上是你的第一个例子。
这是Dave Sexton解释你的第一个例子中会发生什么的解释。
默认情况下,Return使用ImmediateScheduler调用OnNext(1) OnCompleted()。重复不会引入任何并发,所以它看到了 OnCompleted立即,然后立即重新订阅Return。 因为回归中没有蹦床,这种模式会重演, 无限期地阻止当前线程。在此调用订阅 观察永远不会回来。
换句话说,由于无限循环的重入,初始流程永远不会完全完成。所以我们需要一种方法来完成初始流程而不需要重入。
让我们回到上面这篇文章中的RecursiveTest示例,避免无限递归的解决方案是什么?在再次执行RecursiveMethod之前,我们需要RecursiveMethod来完成它的流程。一种方法是建立一个队列并将对RecursiveMethod的调用排入队列,如下所示:
public void RecursiveMethod()
{
if (!_isDone)
{
Enqueue(RecursiveMethod);
_isDone = true;
}
}
这样,初始流程将完成,_isDone将被设置为true,并且当执行下一次对RecursiveMethod的调用时,将不再执行任何操作,从而避免无限递归。这几乎就是Scheduler.CurrentThread将对你的第二个例子所做的。
让我们看看Dave Sexton如何解释你的第二个例子如何运作:
这里,Return正在使用CurrentTheadScheduler来调用OnNext(1) OnCompleted()。重复不会引入任何并发,所以它看到了 立即完成,然后立即重新订阅返回; 然而,第二次订阅Return会安排其(内部) 蹦床上的动作,因为它仍然在蹦床上执行 OnCompleted回调来自第一个计划(外部)动作,因此 重复不会立即发生。这允许重复返回一个 一次性使用,最终调用OnCompleted,取消了 重复处理重复,最后是来自Subscribe的调用 回报。
我的例子再次被简化为易于理解,并不完全是如何运作的。 Here you can see调度程序如何真正起作用。它使用他们称之为Trampoline的东西,它基本上是一个队列,确保没有可重入的调用。因此,所有调用在同一个线程上依次序列化。通过这样做,可以完成初始流程,避免无限重入循环。
希望这更清楚一点:)