2012-03-15 118 views
52

它是有效的SpinWait vs睡眠等待。哪一個使用?

SpinWait.SpinUntil(() => myPredicate(), 10000) 

爲超時10000ms

它是更有效地使用Thread.Sleep輪詢相同的條件 例如東西沿着以下SleepWait的線功能:

public bool SleepWait(int timeOut) 
{ 
    Stopwatch stopwatch = new Stopwatch(); 
    stopwatch.Start(); 
    while (!myPredicate() && stopwatch.ElapsedMilliseconds < timeOut) 
    { 
     Thread.Sleep(50) 
    } 
    return myPredicate() 
} 

我是con如果我們談論超過1秒的超時,那麼SpinWait的所有屈服可能都不是一個好的使用模式?這是一個有效的假設嗎?

你更喜歡哪種方法?爲什麼?還有另一種更好的方法嗎?


更新 - 成爲更具體:

有沒有一種方法,使BlockingCollection脈衝睡眠線程,當它到達界能力?正如Marc Gravel所建議的,我寧願避免忙於等待。

回答

39

最好方法是有一些機制來主動檢測的東西變成真正的(而不是被動地輪詢它有成爲真);這可能是任何類型的等待處理,或者可能是TaskWait,或者可能是event,您可以訂閱以解開自己。當然,如果你做那樣的「等到有事情發生」,那就是仍然不如簡單地把下一個工作完成作爲回調,這意味着:你不需要使用一個線程等待。 Task對此有ContinueWith,或者您可以在event中發起工作。取決於上下文,event可能是最簡單的方法。然而,Task已經提供了您在此討論的大部分內容,包括「等待超時」和「回調」機制。

是的,旋轉10秒並不是很好。如果你想使用類似你當前的代碼,並且如果你有理由期待一個短暫的延遲,但需要允許更長的一個 - 也許SpinWait爲(說)20ms,其餘使用Sleep


重新評論;這裏就是我想掛鉤的「滿」的機制:

private readonly object syncLock = new object(); 
public bool WaitUntilFull(int timeout) { 
    if(CollectionIsFull) return true; // I'm assuming we can call this safely 
    lock(syncLock) { 
     if(CollectionIsFull) return true; 
     return Monitor.Wait(syncLock, timeout); 
    } 
} 

用,在「放回收集」代碼:

if(CollectionIsFull) { 
    lock(syncLock) { 
     if(CollectionIsFull) { // double-check with the lock 
      Monitor.PulseAll(syncLock); 
     } 
    } 
} 
+0

謝謝,我喜歡你的答案,這將是不錯的。假設您正在通過BlockingCollection跟蹤一些資源的使用情況。它們被使用(並從集合中移除)並在它們再次可用以供重用時返回到集合。關閉只能在所有這些都返回到集合中時纔會發生。除了忙碌的等待之外,你會如何去發信號通知關機可以繼續(即收集再次充滿)? – Anastasiosyal 2012-03-15 12:03:31

+0

@Anastasiosyal我會封裝集合,並讓封裝器在完成時做某些事情;實際上,我可能會爲此使用一個「Monitor」(將添加示例) – 2012-03-15 12:07:55

+0

在從BlockingQueue下降的ObjectPool類的析構函數中,逐個彈出BlockingCollection中的所有對象並處理它們,(因爲這是C#,請將檢索到的引用設置爲nil),直到彈出的對象數等於池深度。所有池中的對象都被丟棄了,所以你可以放棄隊列並從Dtor返回來繼續關閉序列。不需要輪詢/忙碌等待!您可能需要在該服務上放一些超時,以便泄漏的對象最終引發異常(或某種情況)。 – 2012-03-15 12:12:33

97

在.NET 4 SpinWait執行CPU密集型在屈服之前旋轉10次迭代。但是在這些週期中的每一個週期之後,它都不會立即返回給調用者,而是立即;相反,它調用Thread.SpinWait通過CLR(基本上是OS)旋轉一段時間。這個時間段最初是幾十納秒,但每次迭代都會加倍,直到10次迭代完成。這樣可以在花在旋轉(CPU密集型)階段的總時間(系統可根據條件(內核數量等)調整)中清晰/可預測。如果SpinWait長時間保持在旋轉屈服階段,它將定期休眠以允許其他線程繼續進行(有關更多信息,請參見J. Albahari's blog)。這個過程是保證讓一個核心忙......

所以,SpinWait限制了CPU密集型紡絲迭代的一組數字後,它產生於每一個旋轉它的時間片(以實際調用Thread.YieldThread.Sleep) ,降低資源消耗。它還將檢測用戶是否正在運行單個核心計算機並在每個週期產生收益(如果情況如此)。

With Thread.Sleep線程被阻塞。但是這個過程並不像CPU那樣昂貴。

+6

+1感謝SpinWait和thread.Sleep之間的清晰度 – Anastasiosyal 2012-03-15 12:18:32

+0

這是從Albahari的網站複製的。它應該被引用! – georgiosd 2017-11-02 14:22:55

+1

它不是逐字複製,而是受到他的網站的影響,因此我引用了他的博客。 – MoonKnight 2017-11-06 11:09:24