2011-05-12 70 views
21

我想同時鎖定兩個對象。 爲什麼我不能像這樣的代碼寫?如何鎖定多個對象?

鎖(obj1,obj2)

我應該一直這樣寫嗎?

lock (obj1) 
{ 
    lock (obj2) 
    { 
    } 
} 

也許這可能會變得更簡單? 有可能會更好,特推出私人對象,並用它來鎖...

+1

爲什麼?我以某種方式認爲你只需要一個鎖。 – 2011-05-12 09:07:14

+0

[如何使用C#中的鎖範圍使用多個變量]可能的重複(http://stackoverflow.com/questions/2874028/how-to-use-multiple-variables-for-a-lock-scope-in​​- c-sharp) – 2016-06-21 20:05:02

回答

22

這是鎖定多個對象的正確方法,是的。

我的猜測是,只允許鎖定語句的單個參數的原因是使鎖定的順序儘可能清晰。

請注意,您必須確保在代碼中的任何位置都以相同的順序執行這兩個鎖,否則您有可能發生死鎖。

您也可以按照您的建議引入單個專用鎖對象,但這會使您的鎖定更加粗糙。這一切都取決於你的需求。如果您有時只需要其中一個鎖,則應該將它們分開(但如上所述,請確保保持鎖定順序)。

+0

你對「同樣的訂單無處不在」的評論是黃金,我在考慮用兩個電匯從同一個兩個帳戶A到B和B到A,你總是會按照字母順序鎖定例如,所以你無論如何鎖定A和B (A)鎖定(B)和鎖定(B)鎖定(A)會死鎖,即使您只進行一次只有一個帳戶參與的操作(如提款),它也能正常工作。我只是想在這裏分享一個實際的例子,它讓我發瘋了一陣子。 – Alex 2017-09-28 21:23:23

13

如果你寫這樣的代碼,你需要確保,你總是這個順序鎖定這兩個對象。否則,您可能會遇到死鎖。

+2

+1。正是我在想什麼。 – Steven 2011-05-12 09:17:52

0

不是鎖定對象本身,而是創建專用對象,稱爲PadLock或類似對象,只鎖定需要的對象。

+0

出於好奇:當我們鎖定「PadLock」時,它是否也鎖定了它的所有內容?因此,如果掛鎖指的是「MyClass」的實例,那麼現在任何其他線程都可以鎖定該實例嗎? – Jakub 2011-05-12 09:07:20

+0

@Jakub:是的,它可以。用'lock'鎖定'PadLock'實例不會鎖定其中包含的任何對象。 – 2011-05-12 09:19:09

0

此處鎖定並不意味着鎖的持續時間其他線程上的其他代碼不能訪問或修改該對象。如果鎖定對象,則其他線程可以同時修改對象。什麼鎖代碼塊允許你做的就是使鎖塊內的代碼是單個條目,即只有一個線程可以執行一次鎖代碼塊,而其他嘗試執行相同代碼塊的線程將不得不等待所有者線程完成執行代碼塊。所以基本上你通常不需要鎖定2個或更多的對象。通過鎖定你的目的是爲了讓代碼塊單個條目

+0

鎖定不會阻止任何其他線程訪問該對象,但它會阻止任何其他線程輸入鎖定在同一對象上的任何代碼塊,而不管它是否是相同的代碼塊。由於代碼塊經常需要獲取鎖的獨立管理的多個資源,所以需要嵌套鎖定語句是非常常見的。 – reirab 2016-06-27 18:45:23

2

你必須這樣做,因爲你寫它的原因,是因爲你不能鎖定在同一時間兩個對象;你一個接一個地鎖定它們(並且保持鎖的順序非常重要,否則你可能會遇到死鎖),最好是儘可能的明確這些東西。

1

這樣做

internal static void DuoEnter(object po1, object po2, int pnTimeOutMs = 1000) 
    { 
     if ((po1 == null) && (po2 == null)) 
      return; 
     int nMaxLoops = 100 * pnTimeOutMs; 
     bool lOneProcessor = Environment.ProcessorCount < 2; 
     for (int nLoops = 0; nLoops < nMaxLoops; nLoops++) 
     { 
      if ((po1 == null) || (po2 == null) || (po1 == po2)) 
      { 
       if (Monitor.TryEnter(po1 ?? po2)) 
        return; 
      } 
      else 
      { 
       if (Monitor.TryEnter(po1)) 
        if (Monitor.TryEnter(po2)) 
         return; 
        else 
         Monitor.Exit(po1); 
      } 
      if (lOneProcessor || (nLoops % 100) == 99) 
       Thread.Sleep(1); // Never use Thread.Sleep(0) 
      else 
       Thread.SpinWait(20); 
     } 
     throw new TimeoutException(
      "Waited more than 1000 mS trying to obtain locks on po1 and po2"); 
    } 

    internal static void DuoExit(object po1, object po2) 
    { 
     if ((po1 == null) && (po2 == null)) 
      return; 
     if (po1 == null || po2 == null || po1 == po2) 
      Monitor.Exit(po2 ?? po1); 
     else 
     { 
      Monitor.Exit(po2); 
      Monitor.Exit(po1); 
     } 
    } 
+1

有趣的想法,但它感覺有點像我偷偷摸摸地穿過窗戶進入一個有登上門的建築物。如果像這樣的東西是一個好主意,爲什麼它不會烘焙到C#本身? – Nathan 2012-06-13 23:29:43

+0

只是寫'lock(obj1){lock(obj2){...}}看起來好多了, – reirab 2016-06-27 18:52:46

23

嗯,這個問題是太舊,但是,這裏是一個緊湊的我想通了,這兩個代碼最終會以相同的編譯語句(這和一個在問題描述):

lock (obj1) lock (obj2) 
    { 
     // your code 
    } 
1

我也遇到了同樣的問題,並寫了這段代碼,可能會幫助你,即使它是遠遠不夠完善:

private void MultiLock(object[] locks, WaitCallback pFunc, int index = 0) 
{ 
    if (index < locks.Count()) 
    { 
     lock (locks[index]) 
     { 
      MultiLock(locks, pFunc, index + 1); 
     } 
    } 
    else 
    { 
     ThreadPool.QueueUserWorkItem(pFunc); 
    } 
} 

接着,就調用這個方法是這樣的:

public object LockedObject1 = new Object(); 
public object LockedObject2 = new Object(); 

public void MyFunction(object arg) 
{ 
    WaitCallback pFunc = delegate 
    { 
     // Operations on locked objects 
    } 

    MultiLock(new object[] {LockedObject1, LockedObject2}, pFunc); 
}