僵局澄清?

时间:2013-02-22 20:00:26

标签: c# .net multithreading deadlock

也许对“死锁”还有其他解释,但AFAIK:

reference

  

两个线程等待所持有的资源时发生死锁   另一个,所以两者都无法进行。

但我在SO上看到了几个答案,声称漫长的等待(不等待对方)也是一个僵局:

Example #1

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;
            }
        }
    }

}
  

请原谅我,但我在这里看到的只是一个尚未发布的纯锁。   这不是两个线程都在等待另一个线程的情况。线程   这里等待主线程完成。


Another example #2

class ILockMySelf
{
    void doThat()
    {
        lock(this){ ... }
    }
}

class WeveGotAProblem
{
    ILockMySelf anObjectIShouldntUseToLock;

    void doThis()
    {
        lock(anObjectIShouldntUseToLock)
        {
            // Following line should run in a separate thread
            // for the deadlock to happen
            anObjectIShouldntUseToLock.doThat();
        }
    }
}

同样在这里。运行doThis的线程不等待将运行doThat

的“单独线程”

问题:

  • 这里涉及死锁吗?

3 个答案:

答案 0 :(得分:3)

我同意David Hope的观点,即死锁的定义很广泛,适用于线程之外的场景。但是,第二个示例不是死锁,因为它没有导致线程“停止”的情况。

要更正第二个示例,请启动新线程强制thread1在thread2上等待。然后,您可以创建死锁:

//Deadlock
public class Program
{
    public static void Main(string[] args)
    {
        WeveGotAProblem p = new WeveGotAProblem();
        p.doThis();//gain a lock on this thread                        
    }        
}

class ILockMySelf
{
    public void doThat()
    {
        //Thread2 waits on Thread1 to release "this"
        lock (this)
        {
            Console.WriteLine("that");
        }
    }
}

class WeveGotAProblem
{
    ILockMySelf anObjectIShouldntUseToLock = new ILockMySelf();

    public void doThis()
    {
        lock (anObjectIShouldntUseToLock)
        {
            Task task = Task.Factory.StartNew(new Action(() => anObjectIShouldntUseToLock.doThat()));
            Task.WaitAll(task);//Thread1 waits on Thread2 to return.  This is important
        }
    }
}

在这里,我们有两个或更多竞争行动,每个等待另一个完成。

  • 线程1正在等待thread2返回。
  • 线程2正在等待thread1在它返回之前释放它所需的资源。

第一个例子(尽管设法......为什么发送一个线程永远睡不着......?)可能会因类似的原因导致死锁。

E.G。

  • Thread1等待Thread.Sleep()返回(它永远不会)
  • Thread2等待Thread1释放可锁定的锁(它永远不会)

操作是线程并且每个线程正在等待不同的结果这一事实只是实现细节。

答案 1 :(得分:2)

关于示例1:

我个人并不认为这是一个僵局,但这只是因为我对“死锁”这个词的语义含义。当然,在这个例子中,任何线程都不会继续,因此它具有与死锁相同的影响,但并不是因为每个线程都在等待另一个线程持有的资源。相反,执行BackgroundMethod(称之为T1)的线程正在等待......等待......什么都不做。执行Reset(称之为T2)的线程无法解除T1 1 的阻塞,因为T2不等待T1持有的资源。因此我不认为这是一个僵局。但是,如果其他人不同意我的话,我不会抱太大的抱怨。

关于示例2:

正如其他人所指出的那样,这也不是僵局。除了这个时间,因为两个线程都不会阻塞。一个原因是因为lock是可重入的。但是,即使你确实在另一个线程上运行doThat,代码仍然不会死锁,因为doThis会立即释放锁。我认为P.Brian.Mackey在他的答案中已经很好地涵盖了这一点,所以我没有理由进一步阐述。

现在有趣的事情

以下是我最喜欢的死锁插图之一,因为不涉及锁定或阻塞方法。问题的微妙之处在于头脑麻木。这里的问题与缺乏记忆障碍有关。线程A等待线程B设置信号标志,同时线程B等待线程A重置它,而线程没有看到对方正在进行的更改,因为编译器,JIT和硬件可以自由优化以非直观的方式读取和写入标志。

在此示例中,简单的bool变量signal 资源。或者,更抽象地说,它是作为资源的内存。由于应用软件和硬件优化的方式,内存操作可以具有获取 - 释放语义。这就是记忆障碍背后的重点。事实上,内存栅栏(或屏障)的正式定义使用术语获取和释放,因为内存是一种可以像任何其他资源一样被操纵的资源。

免责声明:重复性可能因环境和构建配置而异。

public class Example
{
  private bool signal = false;

  void ThreadA()
  {
    while (!signal);
    signal = false;
  }

  void ThreadB()
  {
    signal = true;
    while (signal);
  }
}

1 要完全迂腐,您可以拨打Thread.Interrupt来“戳”Thread.Sleep来电。

答案 2 :(得分:1)

真的,这取决于你对deadlock的定义,因此问题......

根据维基百科,死锁是:

  

死锁是指两种或两种以上竞争行为的情况   每个人都在等待对方完成,因而也没有。

您从任一定义推断活动或操作必须位于不同的线程中,这不是定义所说的。

因此,我在技术上得出结论,示例1不是死锁,因为后台线程永远不会结束(在异常之外),但是示例#2是死锁,因为如果这两个动作不是竞争对于相同的资源,那么执行流程就可以了。

在这两种情况下,都存在编程错误(#2更像是一个错误,其中#1看起来像一个人为的例子),这导致代码无法向前推进。

所以,在任何一种情况下......都有一个问题需要解决,无论单词deadlock的具体意图或用法如何