我有一些代码如下:
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呢!
答案 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对于任何类型的多线程编程都是一个巨大的帮助。我强烈建议至少阅读一次全部内容。将其保存在书签中以备将来参考:)