2012-07-08 34 views
10

(以下項目都有不同的目標,但即時通訊有趣知道他們是如何「暫停」)Thread.sleep vs Monitor.Wait與RegisteredWaitHandle?

問題

Thread.sleep - 它的系統上影響性能,它佔用一個線程,其等待? ?

Monitor.Wait?他們「等」的方式有什麼不同?他們是否等待了一個線程?

RegisteredWaitHandle怎麼樣?該方法接受一個委託,當等待 句柄被髮信號時執行。雖然它正在等待,它不會捆綁一個線程。

所以有些線程暫停,可以被代理人喚醒,而其他人只是等待?旋轉?

有人能讓事情變得更清楚嗎?

編輯

http://www.albahari.com/threading/part2.aspx

enter image description here

回答

8

兩個Thread.SleepMonitor.Wait把線程the WaitSleepJoin state

WaitSleepJoin:線程被阻塞。這可能是因爲調用 Thread :: Sleep或Thread :: Join請求鎖 - 例如,通過調用Monitor :: Enter或Monitor :: Wait - 或等待線程 同步對象,如 ManualResetEvent的。

RegisteredWaitHandle是通過調用RegisterWaitForSingleObject以及使WaitHandle獲得。通常,此類別的所有後代都使用阻止機制,因此調用Wait將再次將線程置於WaitSleepJoin(例如AutoResetEvent)。

這裏的另一個引自MSDN:

的RegisterWaitForSingleObject方法檢查 特定對象的的WaitHandle的當前狀態。如果對象的狀態沒有發出信號, 該方法註冊一個等待操作。線程池中的線程執行 等待操作。該代表由工作人員 線程在對象的狀態變爲信號時執行或超時間隔爲 時執行。

所以池中的線程確實等待的信號。

+0

RegisteredWaitHandle – 2012-07-08 08:44:29

+0

@Royi Namir:查看我的編輯。 – Tudor 2012-07-08 08:52:15

+0

我不認爲你是正確的問候'RegisteredWaitHandle':請參閱我的編輯。 – 2012-07-08 09:03:55

3

ThreadPool.g RegisterWaitForSingleObject確實調用其本地實現最終 QueueUserAPC。參見轉子源(sscli20 \ clr \ src \ vm \ win32threadpool.cpp(1981))。與Wait Thread.Sleep不同,當您使用RegisterWaitForSingleObject時,您的線程不會停止。

取而代之的是,對於此線程,具有用戶模式回調的FIFO隊列被註冊,當線程處於可警告狀態時將被調用。這意味着你可以繼續工作,當你的線程被阻塞時,操作系統將在註冊的回調函數上工作,這樣你的線程就有機會在等待的時候做一些有意義的事情。

EDIT1:

完成分析。在調用RegisterWaitForSingleObject的線程上,當線程處於可警告狀態時,會調用該回調。一旦發生這種情況,調用RegisterWaitForSingleObject的線程將執行CLR回調,該回調會註冊另一個回調,該回調由線程池回調等待線程處理,該回調等待線程僅等待信號回調。此線程池回調等待線程將定期檢查信號回調。

這個等待線程最終會調用QueueUserWorkItem,以便在線程池線程上執行信號回調。

+0

根據我在RegisterWaitForSingleObject文檔中的理解,池中的一個線程實際上會等待處理信號,但不一定是實際工作的線程。 – Tudor 2012-07-08 09:32:32

+0

是的你是對的。查看我的編輯進行全面分析。 – 2012-07-08 13:23:12

+0

所以如果我執行50次RegisteredWaitHandle,將會有一個總是檢查信號狀態的「檢查線程」,但是其他n-1線程池線程是免費的,對嗎? – 2012-07-08 21:09:59

3

Thread.SleepRegisteredWaitHandle工作在不同的級別。讓我嘗試清除它:

進程有多個線程,它們同時執行(取決於OS調度程序)。如果一個線程調用Thread.SleepMonitor.Wait,它不會旋轉 - 它被置於WaitSleepJoin狀態,並且CPU被賦予其他線程。

現在,當您有許多同步工作項目時,您可以使用線程池 - 一種創建多個線程的機制,並使用自己對工作項的理解來調用其線程。在這個模型中,線程池調度程序調用工作線程來完成一些工作,然後返回到池中。如果工作程序線程調用阻塞操作(如Thread.SleepMonitor.Wait),則此線程將被「捆綁」,因爲線程池調度程序不能將其用於其他工作項目。

我對實際的API並不熟悉,但我認爲RegisteredWaitHandle會告訴線程池調度程序在需要時調用一個工作線程 - 而且您自己的線程沒有「束縛」,並且可以繼續工作或返回到線程池。

6

關於ThreadPool.RegisterWaitForSingleObject,這是而不是每個註冊(合併或其他)註冊一個線程。您可以輕鬆地測試:請在LINQPad下面的腳本,調用這個方法20000次:

static ManualResetEvent _starter = new ManualResetEvent (false); 

void Main() 
{ 
    var regs = Enumerable.Range (0, 20000) 
     .Select (_ => ThreadPool.RegisterWaitForSingleObject (_starter, Go, "Some Data", -1, true)) 
     .ToArray(); 

    Thread.Sleep (5000); 
    Console.WriteLine ("Signaling worker..."); 
    _starter.Set(); 
    Console.ReadLine(); 

    foreach (var reg in regs) reg.Unregister (_starter); 
} 

public static void Go (object data, bool timedOut) 
{ 
    Console.WriteLine ("Started - " + data); 
    // Perform task... 
} 

如果代碼捆綁20000個線程5秒的持續時間「等待」,它不可能工作。

編輯 - 響應:

「這是一個證明,但仍然有一個線程這僅檢查 信號線程池中。?」

這是一個實現細節。是的,它可能用一個單獨的線程實現,該線程將回調卸載到託管線程池,儘管不能保證這一點。等待句柄最終由操作系統管理,這也很可能觸發回調。它可能在其內部實現中使用一個線程(或少量線程)。或者使用中斷,它可能不會阻塞單個線程。它甚至可能因操作系統版本而異。這是一個與我們無關的實現細節。

+0

thnaks for reply。這是一個證明。但仍然有一個線程只檢查信號?在線程池? – 2012-07-09 04:52:37

+0

請參閱編輯回覆 – 2012-07-09 06:18:19

3

雖然這是真的RegisterWaitForSingleObject創建等待線程,並不是每個調用都創建一個。

MSDN

需要時

從Raymond Chen的blog

新的等待線程會自動創建:

...而不是花費整個線程,它的成本更接近(但不完全)線程的1/64

因此,使用RegisterWaitForSingleObject通常比創建自己的等待線程更可取。

+0

HI,(ma kore?)創建RegisterWaitForSingleObject確實會創建等待線程,但我們不知道有多少個?所以你認爲這個池沒有一個線程來檢查信號狀態?這是什麼意思1/64的線程?它的一小部分....請解釋 – 2012-07-09 06:52:28

+0

正如Raymond解釋的那樣,它使用'WaitForMultipleObjects',所以一個等待線程用於多個對象(最多64個)。 – 2012-07-09 06:59:16