为什么monitor.exit不适用于其他工作?

时间:2016-11-05 10:05:31

标签: c# multithreading asynchronous

我有一些代码如下:

static void Main( string[] args )
    {
        var async = new object();

        Monitor.Enter( async );

        Task.Run( async () =>
        {
            await Task.Delay( 5000 );
            Monitor.Exit( async );
        } );

        Monitor.Wait( async );
        Monitor.Exit( async );
    }

但它会永远阻挡Monitor.Wait。我可以通过使用信号量来解决这个问题。但我想知道它为什么会发生,那么mutex,spinlock,barrier.thks呢!

1 个答案:

答案 0 :(得分:2)

Monitor具有线程关联性。您无法从其他线程退出。

但不清楚你到底想要做什么。您已经在异步方法中,调用另一个异步方法。只需使用await - 只需设置同步上下文,即可在控制台应用程序中正确使用await。或者,如果您不担心死锁,请使用Task.Wait

至于Monitor.Wait,它并不像你认为的那样做。等待是一个信号,而不是退出监视器。例如:

static readonly object syncObject = new object();

void Thread1()
{
  lock (syncObject) Monitor.Wait(syncObject);
}

void Thread2()
{
  lock (syncObject) Monitor.Pulse(syncObject);
}

在这种情况下,两种方法都在不同的线程上执行。如果Thread1先运行,它将锁定,等待一个信号(这将在出现信号之前退出锁定)并在给出信号后重新获取锁定。 Thread1使用Monitor.Pulse方法提供信号。请注意,在这两种情况下,锁定都在单个线程上进行,并在同一个线程上退出。

这种机制不是很容易正确使用,而且有点慢,所以你不会看到太多。

此外,Task.Run使用的主题不属于您。在线程池线程上使用阻塞代码通常是不可取的 - 确保您理解您所做的权衡。更重要的是,任务没有线程亲和力 - 因此使用像监视器这样的线程仿射同步原语是相当冒险的:)在你的情况下,即使你把锁定在Task.Run而不是外部,它Monitor.Exit可能会失败,因为您可能在await之后获得了不同的主题。

别忘了关于多线程的最棘手的部分并不是它不起作用 - 它有一个丑陋的倾向,大部分都在工作并且在奇怪的场景中失败(这在实践中一直发生,请注意)。测试不足以让您对多线程代码在实践中的行为有很大的信心。你得到的只是很小的,几乎不可能重现错误。你在这里很幸运 - 你的代码可靠地失败;对于大多数多线程问题,情况肯定不是这样:)

Joe Albahari's introduction to threading对于任何类型的多线程编程都是一个巨大的帮助。我强烈建议至少阅读一次全部内容。将其保存在书签中以备将来参考:)