2010-06-08 62 views
11

也許這個問題聽起來很愚蠢,但我不明白'關於線程和鎖定的事情,我想獲得確認(here's why I ask)。將lock()語句阻塞進程/ appdomain中的所有線程?

因此,如果我在同一時間有10個服務器和10個請求來到每個服務器,那麼整個服務器場的請求就是100個。沒有鎖定,那100請求到數據庫。

如果我做這樣的事情:

private static readonly object myLockHolder = new object(); 
if (Cache[key] == null) 
{ 
    lock(myLockHolder) 
    { 
     if (Cache[key] == null) 
     { 
     Cache[key] = LengthyDatabaseCall(); 
     } 
    } 
} 

多少數據庫請求我會做什麼? 10? 100?或者像我有線程一樣多?

+0

謝謝大家的出色解釋,我測試過它,它的工作原理與你所有的建議 - 10分貝的請求。 – MikeJ 2010-06-09 10:40:08

回答

10

你有對象的層次結構:

  • 你有服務器(10)
  • 在每個服務器上,你必須處理(大概只有1 - 您的服務/應用程序池)
  • 在每一個過程中,您已經線程(可能很多)

您的代碼將禁止在同一進程中的線程在同一臺服務器訪問修改Cache對象同時。您可以跨進程甚至跨服務器創建鎖,但在向上移動層次結構時,成本會增加很多。

使用lock聲明實際上並不任何線程。但是,如果一個線程正在執行鎖中的代碼(即在lock語句後面的代碼塊中),那麼任何想要取得鎖並執行相同代碼的其他線程都必須等待,直到持有鎖的第一個線程離開代碼塊並釋放鎖定。

C#lock語句使用Windows critical section這是一種輕量級鎖定機制。如果您想鎖定進程,則可以使用mutex。要鎖定跨服務器,您可以使用數據庫或共享文件。

正如dkackman指出的,.NET有一個AppDomain的概念,它是一種輕量級的過程。每個進程可以有多個AppDomain。 C#lock語句只鎖定單個AppDomain中的資源,並且適當的層次結構描述將包括進程下方和線程上方的AppDomain。然而,通常你在一個進程中只有一個AppDomain,使得這個區別有些不相關。

+2

稍微更正「您的代碼只會禁止同一AppDomain內的線程」。 C#靜態綁定到一個AppDomain,而不是一個進程。 – dkackman 2010-06-08 13:18:08

1

lock將阻止該應用程序中的所有線程訪問myLockHolder對象。

所以,如果你有10個應用程序運行的實例,你會得到10個請求到服務器,而每個對象被鎖定。退出鎖定聲明的那一刻,下一個請求將在應用過程中,但只要Cache[key]null,它不會訪問數據庫..

你得到實際的請求數量取決於發生了什麼在這裏:

if (Cache[key] == null) 
    { 
    Cache[key] = LengthyDatabaseCall(); 
    } 

如果LengthyDatabaseCall();失敗,下一個請求會嘗試訪問數據庫服務器和檢索信息一樣,所以真的是你最好的情況是,只會有10個服務器的請求。

0

只有在另一個線程正在使用它時需要訪問您的共享變量的線程纔會進入等待狀態。

有多少是在任何時候都很難確定。

2

C#lock語句鎖定對象(使用new object()創建的對象)的特定實例。對象(大多數情況下)不在AppDomain之間共享,因此如果您有10臺服務器,則可以使用該代碼段同時運行10個線程來訪問數據庫。

0

你的DB將獲得10個請求,與賠率是好的快得多要求2-10跑得比要求1

2

鎖不阻塞線程。 它正在鎖定某個對象的某個實例。試圖訪問它的每個線程都被阻止。 因此,在您的情況下,每個嘗試訪問myLockHolder的線程都將被鎖定,而不是所有的線程。 換句話說,我們可以說Lock statement是使用Critical Section的語法糖。

就像你可以在MSDN看到:

鎖(表達式)語句塊

其中:

表達指定要鎖定的對象。表達式必須是 是一個引用類型。典型地, 表達要麼是此,如果 要保護一個實例變量, 或typeof運算(類),如果要 保護一個靜態變量(或者如果 臨界段在一個靜態 方法時發生給定班級)。

聲明塊關鍵部分的聲明。