2013-03-16 55 views
1

我看到傑弗裏裏氏video(點擊查看確切的行),他說:爭用資源 - 不總是使用鎖?

它始終是更好地使用Monitor.Enter和Monitor.Lock過 事件等待句柄或信號等原因他們(monitor.X)使用內核對象,但他們只有使用它們,如果有爭用。和如果沒有爭用,他們不使用這些對象。

我也許在這裏的東西,但是當我做:

lock(myObj) 
{ 
... 
} 

推測,有可能是誰想要進入臨界區的多個線程。
那麼,根據上面的信息,如果沒有爭用,鎖不會被使用? (如果另一個線程即將輸入1毫秒後怎麼辦?)

+0

你爲什麼在報價中強調這些單詞?他強調他們嗎? – 2013-03-16 09:06:04

+0

@AshBurlaczenko我相信這會幫助其他人看到我的問題所涉及的重要詞彙(對我而言)。 (你對此感到不安嗎?歡迎您將其刪除) – 2013-03-16 09:10:46

+0

只是,如果你引用某人的話,你應該把它放在他們說的話。如果你認爲這有幫助,那很好。 – 2013-03-16 09:12:38

回答

2

那麼,根據上面的信息,如果沒有爭用,鎖不會被使用? (如果另一個線程即將輸入1 ms後會怎樣?)

正確。然後有爭用,另一個線程將不得不進入內核。此外,擁有該鎖的線程在解鎖時也必須進入內核。

的操作那種看起來像這樣:

鎖:

  1. ,嘗試以原子設置從解鎖用戶空間鎖變量來鎖定。如果我們這樣做,停下來,我們完成了。

  2. 遞增用戶空間爭用計數。

  3. 設置內核空間鎖定爲鎖定狀態。

  4. 嘗試自動設置用戶空間鎖定變量從解鎖到鎖定。如果我們這樣做,減少用戶空間爭用計數並停止,我們就完成了。

  5. 在內核中鎖定內核鎖。

  6. 轉到步驟3。

解鎖:

  1. 原子方式設置從鎖定的用戶空間鎖變量解鎖。

  2. 如果用戶空間爭用計數爲零,請停止,我們就完成了。

  3. 設置內核鎖定爲解鎖。

注意如果沒有爭用,鎖定操作只涉及步驟1,解鎖操作只涉及步驟1和2,所有這些操作都發生在用戶空間中。

+0

請澄清:起初,在時間T沒有爭用!因爲只有一個贏家!首先獲得鎖定的線程。所以如果我是贏家,我怎麼知道是否有另一個線程後來呢?我的意思是,作爲一個贏家,我會永遠看到 - 沒有爭論!只有第二個線程纔會看到資源已被佔用......請你解釋一下嗎? – 2013-03-16 11:44:11

+0

@RoyiNamir:如果另一個線程在解鎖時晚點來臨,贏家只關心。在我的示例實現中,失敗線程通過增加解鎖操作中檢查的爭用計數來告訴獲勝者。 (請參閱鎖定的步驟2和解鎖的步驟2.)獲勝(無爭用)鎖定僅爲步驟1.失敗(爭用)鎖定爲1,2,3,4,5,6,3,4。獲勝(沒有爭用)解鎖只是步驟1和2.失敗(爭用)解鎖是1,2,3。沒有任何「獲勝」路徑有任何內核操作。 – 2013-03-16 11:45:33

+0

謝謝。所以,jeffery其實說的是:如果線程(第1行)試圖將_varlock從_unlock_設置爲_lock_併成功,那麼實際上DONT LOCK,即:不使用內核對象。但如果它沒有這樣做,所以進入鎖定模式...正確? – 2013-03-16 11:52:37

2

lock聲明只是語法糖,它使用Monitor.EnterMonitor.Exit作爲其實現。

Monitor該函數本身正在使用Condition Variables實現。它們的實現意味着它們不需要分配內核對象,除非實際上存在爭用鎖的情況。發生這種情況時,他們必須繼續並分配一個內核對象。

即使存在鎖爭用,它們也不會立即分配內核對象。相反,他們會「旋轉」(只是短暫地坐着一圈),希望鎖定變得自由。只有當它沒有變得自由時,它纔會繼續分配/使用內核對象。

請注意,一些新的同步類,如ManualResetEventSlim也採用這種方法。 (一般地,在端部用「苗條」任何同步類來實現的。)

see this thread另外。

要直接回答你關於lock的問題:是的,如果沒有爭用,或者爭用只持續很短時間,那麼爲了使用內核對象將不會轉換到內核模式。只有當爭用持續一段時間後纔會發生向內核模式的轉換。

3

是的,Monitor類在CLR中進行了很好的優化。它是非常便宜便宜,如果沒有其他線程已經擁有監視器。你甚至不支付額外的存儲空間,鎖定狀態被存儲在Object中的一個字段中,每個對象都有可用的對象頭部分。

Monitor.Enter()方法通過首先檢查鎖是否已被同一線程擁有,從而避免輸入OS代碼。這使得它重新進入,如果是這樣的話,它只會增加鎖計數。接下來試圖通過使用Interlocked.CompareExchange()這個等價於任何處理器上非常便宜的基元來獲得鎖。它的x86版本值得注意,因爲實際上根本沒有使用總線鎖,您可以在this answer中看到它的代碼。

如果這樣做沒有起作用,那麼操作系統需要介入,因爲現在重要的是它可以使線程上下文切換在鎖釋放時喚醒線程備份。 Windows非常喜歡挑選一個等待OS同步對象的線程,這可以確保它儘快重新開始運行。基礎對象是一個簡單的事件,一個非常便宜的操作系統對象。它還處理公平性,將等待的線程放入隊列並以先入先出的順序發佈。我在this answer中記錄了基礎CLR代碼。