2015-06-23 61 views
8

閱讀explanation爲什麼調度程序:立即與CurrentThread

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

從來沒有完成,但

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

按預期工作後。我仍然困惑,我不知道爲什麼currentThread實際上解決了這個問題。有人可以給出明確的解釋嗎?

+0

我已經嘗試過兩種方法,他們都完成對我來說很好。你可以請張貼重現問題的代碼嗎? – Enigmativity

+1

請參閱此[post](https://social.msdn.microsoft.com/Forums/zh-CN/7f75482f-eff2-4938-9491-47fe870989e8/currentthreadscheduler-vs-immediatescheduler?forum=rx)。 –

+0

@ Enigmativity第一個在Linqpad中鎖定我,儘管它打印出值 –

回答

4

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。 由於Return中沒有蹦牀,這種模式會自動重複, 無限期地阻止當前線程。調用這個 訂閱永遠不會返回。

換句話說,由於無限循環的重入,初始流動永遠不會完全完成。所以我們需要一種方法來完成最初的流程而不需要重新進入。

讓我們回到上面這篇文章中的RecursiveTest示例,避免無限遞歸的解決方案是什麼?在執行RecursiveMethod之前,我們需要RecursiveMethod來完成它的流程。要做到這一點的方法之一是有一個隊列和排隊這樣的調用RecursiveMethod:

public void RecursiveMethod() 
{ 
    if (!_isDone) 
    { 
     Enqueue(RecursiveMethod); 
     _isDone = true; 
    } 
} 

這種方式,初始流動將完成,_isDone將被設置爲true,何時RecursiveMethod下一個電話是執行,沒有什麼會得到執行,避免無限遞歸。這幾乎是Scheduler.CurrentThread對第二個例子的作用。

讓我們來看看戴夫·塞克斯頓如何解釋你的第二個例子是如何工作的:

這裏,返回使用CurrentTheadScheduler調用OnNext(1),然後 OnCompleted()。重複不會引入任何併發性,所以它立即看到 OnCompleted,然後立即重新訂閱Return; 但是,此第二次訂閱返回計劃在蹦牀上的(內部) 操作,因爲它仍在 上執行第一個計劃(外部)操作的OnCompleted回叫,因此 重複不會立即發生。這允許重複返回 一次性到Take,最終調用OnCompleted,通過處理重複取消 重複,並最終返回訂閱 的呼叫。

我的例子再一次被簡化,使其易於理解,它不完全是如何工作的。 Here you can see調度程序是如何工作的。它使用他們所稱的蹦牀,它基本上是一個確保沒有可重入呼叫的隊列。因此所有的調用都是在同一個線程上依次序列化。通過這樣做,可以完成初始流程,避免無限重入循環。

希望這有點更清楚:)

+0

感謝這個答案,它對我來說非常清楚。 – GiantSquid