2

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

参考

两个线程都等待对方持有的资源时,就会发生死锁,因此两个线程都无法继续。

但是我在这里看到了几个关于 SO 的答案,声称长时间等待(不相互等待)也是一个僵局:

示例 #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;
            }
        }
    }

}

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


另一个例子#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

问题 :

  • 这里涉及死锁吗?
4

3 回答 3

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 正在等待线程 2 返回。
  • 线程 2 正在等待线程 1 释放它需要的资源,然后才能返回。

第一个示例(虽然是人为的……为什么要让一个线程永远休眠……?)可能由于类似的原因导致死锁。

例如

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

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

于 2013-02-22T20:40:31.833 回答
2

关于示例 1:

我个人不认为这是一个死锁,但这只是因为我对“死锁”这个词的语义。当然,在这个例子中,两个线程都不会继续,因此它与死锁具有相同的影响,但这并不是因为每个线程都在等待另一个线程持有的资源。相反,正在执行的线程BackgroundMethod(称为 T1)只是在等待……并且等待……一无所获。由于 T2 没有等待 T1 持有的资源,因此执行的线程Reset(称为 T2)无法采取任何操作来解除对 T1 1的阻塞。正是出于这个原因,我不认为这是一个僵局。但是,如果其他人不同意我的观点,我不会抱怨太多。

关于示例 2:

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

现在是有趣的东西

以下是我最喜欢的死锁插图之一,因为不涉及锁定或阻塞方法。问题的微妙之处令人麻木。这里的问题与没有内存屏障有关。线程 A 等待线程 B 设置信号标志,同时线程 B 一直等待线程 A 重置它,而两个线程都没有看到另一个线程所做的更改,因为编译器、JIT 和硬件可以自由优化以非直观的方式读取和写入标志。

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

免责声明:再现性可能因您的环境和构建配置而异。

public class Example
{
  private bool signal = false;

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

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

1为了完全迂腐,你可以打电话Thread.Interrupt来“戳”Thread.Sleep电话。

于 2013-02-23T00:42:06.413 回答
1

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

根据维基百科,死锁是:

死锁是这样一种情况,其中两个或多个相互竞争的动作都在等待对方完成,因此永远都不会。

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

所以,从技术上讲,我会得出结论,示例 1 不是死锁,因为无论如何后台线程都不会结束(在异常之外),但示例 2 是死锁,因为如果两个动作没有竞争相同资源,然后执行流程就可以了。

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

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

于 2013-02-22T20:10:31.940 回答