2009-05-21 55 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(); 
    } 
}