示例代码:
class Program
{
static readonly object locker = new object();
static void Main(string[] args)
{
Func();
Func();
Thread.Sleep(6000);
}
static void Func()
{
Monitor.Enter(locker);
Action act = () =>
{
Thread.Sleep(2000);
};
act.BeginInvoke(a =>
{
Console.WriteLine("exiting..");
Monitor.Exit(locker);
}, null);
Console.WriteLine("Func done...");
}
}
理想情况下,控制台会打印出来:
Func done...
exiting...
Func done...
exitting...
但是,我得到了:
Func done...
Func done...
exitting...
然后Monitor.Exit抛出异常
从非同步代码块调用对象同步方法。
这里有什么错误?实现这一目标的首选方法是什么?
答案 0 :(得分:4)
Monitor.Enter
和Monitor.Exit
调用必须在同一个线程上进行。在您的示例中,您在UI线程上调用Monitor.Enter
,在为Monitor.Exit
触发的异步调用创建的线程上调用BeginInvoke
。
如果你想等待Func
内的异步操作完成,你可以这样做:
class Program
{
static void Main(string[] args)
{
Func();
Func();
Thread.Sleep(6000);
}
static void Func()
{
Action act = () =>
{
Thread.Sleep(2000);
};
IAsyncResult actAsyncResult = act.BeginInvoke(a =>
{
Console.WriteLine("exiting..");
}, null);
Console.WriteLine("Func done...");
act.EndInvoke(actAsyncResult);
}
}
然而,在您的场景中,您可以只是同步调用委托。
答案 1 :(得分:2)
Monitor.Enter(locker)在当前线程上,Monitor.Exit位于不同的线程上,因为它是从当前线程调用的。
因此,您还需要使用Monitor.Wait和Monitor.Pulse,但在您的情况下,ManualResetEvents更容易。
答案 2 :(得分:0)
我认为您可以使用ManualResetEvent class等待活动完成。对不起,我对Monitor没有任何经验。但我使用ManualResetEvent / AutoResetEvent类来测试回调。
答案 3 :(得分:0)
线程不关闭监视器
Monitor.Exit(locker);
这是一个问题
答案 4 :(得分:0)
此错误非常具有误导性。它实际上并不意味着它的意思。它实际上意味着在您在同步对象上调用Monitor.Exit
之前调用Monitor.Enter
。
您的Monitor.Exit
通话发生在与Monitor.Enter
通话不同的线程上 - 两人没有看到对方的同步对象。