2009-05-21 71 views
24

我讀過幾篇文章和文章,說lock(this),lock(typeof(MyType)),lock("a string")都是不好的做法,因爲另一個線程可能鎖定相同的密鑰並導致死鎖。爲了理解這個問題,我試圖創建一些示例代碼來說明僵局,但一直未能圍繞這個問題進行解決。通過使用鎖來說明死鎖的示例代碼(this)

有人可以寫一段簡短的代碼來說明這個經典問題嗎?請保持簡短,我只能以更小的塊來消化代碼。

編輯: 我認爲lassevk總結得很好;真正的問題是你失去了對鎖的控制。一旦發生這種情況,您無法控制調用鎖的順序,並且允許潛在的死鎖情況。

lock(this),lock(typeof(MyType))等都是你選擇了一個無法控制的鎖。

回答

31

死鎖只會發生,如果你有多個鎖。您需要兩個線程都擁有其他需要的資源的情況(這意味着必須至少有兩個資源,並且兩個線程必須嘗試以不同的順序獲取它們)

所以一個簡單的示例:

// thread 1 
lock(typeof(int)) { 
    Thread.Sleep(1000); 
    lock(typeof(float)) { 
    Console.WriteLine("Thread 1 got both locks"); 
    } 

} 

// thread 2 
lock(typeof(float)) { 
    Thread.Sleep(1000); 
    lock(typeof(int)) { 
    Console.WriteLine("Thread 2 got both locks"); 
    } 
} 

假設兩個線程都每個人的一秒鐘內啓動,他們都將有時間搶得頭鎖在任何人到達內閘門。沒有Sleep()調用,其中一個線程很可能有時間在另一個線程開始之前獲取並釋放這兩個鎖。

+2

哈哈,我寫了完全相同的樣本,而你發佈它:)但我選擇了長和int – Maghis 2009-05-21 17:41:43

3

當然,這裏你去。

請注意,死鎖的常見示例是當您獲取多個鎖並且兩個或更多個線程最終彼此等待時。

例如,兩個線程鎖定是這樣的:

Thread 1    Thread 2 
Lock "A"    Lock "B" 
Lock "B"    Lock "A" <-- both threads will stop dead here 
            waiting for the lock to be come 
            available. 

然而,在這個例子中我沒有打擾,我只是讓一個線程鎖定下去。你真的不想失去對你的鎖的控制,所以雖然這是一個人爲的例子,後臺線程可以完全阻止這樣的主線程的事實是不好的。

using System; 
using System.Threading; 

namespace ConsoleApplication7 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      LockableClass lockable = new LockableClass(); 
      new Thread(new ParameterizedThreadStart(BackgroundMethod)).Start(lockable); 
      Thread.Sleep(500); 
      Console.Out.WriteLine("calling Reset"); 
      lockable.Reset(); 
     } 

     private static void BackgroundMethod(Object lockable) 
     { 
      lock (lockable) 
      { 
       Console.Out.WriteLine("background thread got lock now"); 
       Thread.Sleep(Timeout.Infinite); 
      } 
     } 
    } 

    public class LockableClass 
    { 
     public Int32 Value1 { get; set; } 
     public Int32 Value2 { get; set; } 

     public void Reset() 
     { 
      Console.Out.WriteLine("attempting to lock on object"); 
      lock (this) 
      { 
       Console.Out.WriteLine("main thread got lock now"); 
       Value1 = 0; 
       Value2 = 0; 
      } 
     } 
    } 

} 
+2

評論其他答案正確地表明,「死鎖」就是我在多重鎖定場景中所描述的,但這裏的主要問題是你失去了對鎖定的控制權,而不是它是否是真正的死鎖。你永遠不想失去對鎖的控制。 – 2009-05-21 17:29:45

+0

無限期鎖定的線程是否被認爲是死鎖情況? – 2013-02-22 19:33:56

-1

問題是,鎖(「一個字符串」)鎖定在一個單身人士。這意味着使用相同鎖的其他對象可能是無限的等待。

例如:

using System; 
using System.Threading; 

namespace ThreadLock 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      lock ("my lock") 
      { 
       ManualResetEvent evt = new ManualResetEvent(false); 
       WorkerObject worker = new WorkerObject(evt); 
       Thread t = new Thread(new ThreadStart(worker.Work)); 
       t.Start(); 
       evt.WaitOne(); 
      } 
     } 
    } 

    class WorkerObject 
    { 
     private ManualResetEvent _evt; 
     public WorkerObject(ManualResetEvent evt) 
     { 
      _evt = evt; 
     } 
     public void Work() 
     { 
      lock ("my lock") 
      { 
       Console.WriteLine("worked."); 
       _evt.Set(); 
      } 
     } 
    } 
} 

在這種情況下,調用代碼創建一個字符串的鎖然後進行工人對象。 Work()中的worker對象鎖定在同一個字符串中,這是C#中的一個單例。它最終陷入僵局,因爲主叫方擁有鎖定,正在等待永遠不會到來的信號。

1

這是非常標準的壞處。不按順序抓住鎖,然後與鎖一起睡覺。兩件不好的事情要做。:)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading; 

namespace DeadLock 
{ 
    public class Program 
    { 
     static void Main(string[] args) 
     { 
      var ddt = new DontDoThat(); 

      ddt.Go(); 
     } 
    } 

    public class DontDoThat 
    { 
     private int _badSharedState = 0; 
     private readonly object _lock1 = new object(); 
     private readonly object _lock2 = new object(); 

     public void Go() 
     { 
      new Thread(BadGuy1).Start(); 
      new Thread(BadGuy2).Start(); 

      Console.WriteLine("Leaving Go!"); 
     } 

     public void BadGuy1() 
     { 
      lock (_lock1) 
      { 
       Thread.Sleep(100); // yeild with the lock is bad 
       lock (_lock2) 
       { 
        _badSharedState++; 
        Console.Write("From Bad Guy #1: {0})", _badSharedState); 
       } 
      } 
     } 
     public void BadGuy2() 
     { 
      lock (_lock2) 
      { 
       lock (_lock1) 
       { 
        _badSharedState++; 
        Console.Write("From Bad Guy #2: {0})", _badSharedState); 
       } 
      } 
     } 
    } 
} 
3

這個想法是,你永遠不應該鎖定你無法控制誰有權訪問的東西。

類型對象是每個.net代碼段都可見的單例,並且您無法控制從外部對「this」對象執行的鎖定。

同樣的事情是因爲字符串:因爲字符串是不可變的,框架只保留一個「硬編碼」字符串的實例,並把它們放在一個池中(字符串被認爲是被實現的),如果你在你的編碼字符串「你好」,你將永遠得到相同的基礎。

請看下面的例子:你在超級私人電話寫道只是線程1,而線程2是由你所使用的後臺線程一些庫調用...

void Thread1() 
{ 
    lock (typeof(int)) 
    { 
    Thread.Sleep(1000); 
    lock (typeof(long)) 
     // do something 
    } 
} 

void Thread2() 
{ 
    lock (typeof(long)) 
    { 
    Thread.Sleep(1000); 
    lock (typeof(int)) 
     // do something 
    } 
} 
0
class Character 
{ 
    public Character Other; 
    public string Name; 
    private object locker = new object(); 

    public Character(string name) 
    { 
     Name = name; 
    } 

    public void Go() 
    { 
     lock (locker) 
     { 
      Thread.Sleep(1000); 
      Console.WriteLine("go in {0}", Name); 
      Other.Go(); 
     } 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Character a = new Character("A"); 
     Character b = new Character("B"); 
     a.Other = b; 
     b.Other = a; 

     new Thread(a.Go).Start(); 
     b.Go(); 

     Console.ReadLine(); 
    } 
}