调度程序:立即与CurrentThread

时间:2015-06-23 23:40:49

标签: system.reactive

阅读explanation后的原因

Observable.Return(5)
  .Repeat()
  .Take(1)

永远不会完成,但是

Observable.Return(5, Scheduler.CurrentThread)
  .Repeat()
  .Take(1)

按预期工作。我仍然感到困惑,我无法告诉为什么currentThread实际上解决了这个问题。有人可以给出明确的解释吗?

1 个答案:

答案 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的东西,它基本上是一个队列,确保没有可重入的调用。因此,所有调用在同一个线程上依次序列化。通过这样做,可以完成初始流程,避免无限重入循环。

希望这更清楚一点:)