2015-10-23 181 views
3

如果我在Thread.Sleep(0)方法中评论或传递0,那么就没有死锁。在其他情况下,存在僵局。 uptask由线程轮询中的线程执行,并且需要一些时间。同时,主线程获取lockB,lockA并打印字符串并释放锁。之后uptask开始运行,它看到lockA和lockB是空闲的。所以在这种情况下没有死锁。但是如果我在睡眠主线程的同时uptask前进,并看到lockB被锁定并发生死锁。任何人都可以更好地解释或验证这是否是原因?使用Thread.Sleep解决锁定死锁

class MyAppClass 
{ 
    public static void Main() 
    { 
     object lockA = new object(); 
     object lockB = new object(); 
     var uptask = Task.Run(() => 
     { 
      lock (lockA) 
      { 
       lock (lockB) 
       { 
        Console.WriteLine("A outer and B inner"); 
       } 
      } 
     }); 
     lock (lockB) 
     { 
      //Uncomment the following statement or sleep with 0 ms and see that there is no deadlock. 
      //But sleep with 1 or more lead to deadlock. Reason? 
      Thread.Sleep(1); 
      lock (lockA) 
      { 
       Console.WriteLine("B outer and A inner"); 
      } 
     } 
     uptask.Wait(); 
     Console.ReadKey(); 
    } 
} 

回答

1

https://msdn.microsoft.com/en-us/library/d00bd51t(v=vs.110).aspx “millisecondsTimeout” 类型:System.Int32 的量,thread悬浮的milliseconds数。 如果millisecondsTimeout参数的值为零,则线程将其时间片的其余部分放弃到任何准备运行的优先级相同的线程。如果没有其他相同优先级的线程已准备好运行,那么当前线程的执行不会被挂起。“

5

你真的不能依靠Thread.Sleep来防止死锁,它在你的环境中工作了一段时间。可能无法正常工作所有的时间和在其他环境中可能无法正常工作。

由于要获得以相反的顺序锁,则死锁的可能性是存在的。

为了防止死锁,请确保您获得按顺序锁定(例如lockA然后在两个线程中锁定)

为什么会发生这种情况,我最好猜测的是,如果您不睡觉,那么主线程将在其他线程(线程池)获取lockA之前获取并释放这两个锁。请注意,在线程池上调度和运行任务需要一些时间。在大多数情况下,这是可以忽略的。但它是你的情况,它有所作为。

为了验证这一点,添加以下行uptask.Wait()前右:

Console.WriteLine("main thread is done"); 

而此行从线程池线程获得洛卡后:

Console.WriteLine("Thread pool thread: obtained lockA"); 

如果不存在僵局,在线程池线程将消息打印到控制台之前,您将看到第一条消息输出到控制台。

+0

这不是生产代码,我只是在做实验。为什么在我的环境中睡1秒会造成死锁,如果没有睡眠,就没有死锁?我试图找到答案。 – RotatingWheel

+0

@RotatingWheel,看我的更新 –

2

你有两个线程。主线程和执行任务的线程。如果主线程能够执行lockB并且任务线程能够执行lockA,那么你就有一个死锁。你不应该以不同的顺序锁定两个不同的资源,因为这会导致死锁(但我期望你已经知道这是基于你的问题的抽象性质)。

在大多数情况下,任务线程将开始略有延迟,然后主线程将获得两个锁,但如果你插入和lockB一个Thread.Sleep(1)之间lockA则任务线程能够在主线程和BAM之前得到lockA!你有一个僵局。

但是,Thread.Sleep(1)不是获得死锁的必要条件。如果操作系统决定安排任务线程的方式使其能够在主线程之前获得lockA,则会导致死锁,并且仅仅因为快闪式计算机上没有死锁,您可能会遇到其他计算机上的死锁处理资源。

这里有一个图直观地解释了为何延迟增加感染死锁的可能性:

Deadlock visualization

0

马丁Liversage简洁的回答了这个问题。

为了解释,实验中的代码很容易出现死锁,即使没有Thread.Sleep()语句。如果没有Thread.Sleep()语句,死锁发生的概率窗口非常小,并且可能发生了无数事件。这是您省略Thread.Sleep()语句时没有经历过的原因。通过在第19行添加任何耗时的逻辑(即:Thread.Sleep),可以展开此窗口并增加死锁的概率。

此外,通过在不同的硬件/操作系统上运行代码,可以扩展/减少此窗口,其中任务调度可能不同。