C# 关于获取锁的说明
我已经用C#编码了很长一段时间,但是这个锁定序列对我来说没有任何意义。我对锁定的理解是,一旦通过C# 关于获取锁的说明,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,我已经用C#编码了很长一段时间,但是这个锁定序列对我来说没有任何意义。我对锁定的理解是,一旦通过lock(object)获得锁,代码必须退出锁作用域才能解锁对象 这就引出了我手头的问题。我删掉了下面的代码,它恰好出现在我的代码中的动画类中。该方法的工作方式是将设置传递给该方法并进行修改,然后传递给另一个重载方法。另一个重载方法将把所有信息传递给另一个线程,以便以某种方式处理并实际设置对象的动画。动画完成后,另一个线程调用OnComplete方法。实际上,这一切都非常有效,但我不明白为什么 另一个
lock(object)
获得锁,代码必须退出锁作用域才能解锁对象
这就引出了我手头的问题。我删掉了下面的代码,它恰好出现在我的代码中的动画类中。该方法的工作方式是将设置传递给该方法并进行修改,然后传递给另一个重载方法。另一个重载方法将把所有信息传递给另一个线程,以便以某种方式处理并实际设置对象的动画。动画完成后,另一个线程调用OnComplete
方法。实际上,这一切都非常有效,但我不明白为什么
另一个线程能够调用OnComplete
,获得对象上的锁,并向原始线程发出信号,表示它应该继续。既然对象被锁在另一个线程上,代码是否应该在此时冻结
因此,这不是需要帮助来修复我的代码,而是需要澄清它为什么工作。感谢您对理解的任何帮助
public void tween(string type, object to, JsDictionaryObject properties) {
// Settings class that has a delegate field OnComplete.
Tween.Settings settings = new Tween.Settings();
object wait_object = new object();
settings.OnComplete = () => {
// Why are we able to obtain a lock when the wait_object already has a lock below?
lock(wait_object) {
// Let the waiting thread know it is ok to continue now.
Monitor.Pulse(wait_object);
}
};
// Send settings to other thread and start the animation.
tween(type, null, to, settings);
// Obtain a lock to ensure that the wait object is in synchronous code.
lock(wait_object) {
// Wait here if the script tells us to. Time out with total duration time + one second to ensure that we actually DO progress.
Monitor.Wait(wait_object, settings.Duration + 1000);
}
}
如文件所述,释放调用它的监视器。因此,当您尝试在OnComplete
中获取锁时,将不会有另一个线程持有该锁
当监视器被触发(或呼叫超时)时,它会在返回之前重新获取监视器
从文档中:
释放对象上的锁并阻止当前线程,直到它重新获得锁为止
我写了一篇关于这一点的文章: 发生的事情比我们想象的还要多 记住:
lock(someObj)
{
int uselessDemoCode = 3;
}
相当于:
Monitor.Enter(someObj);
try
{
int uselessDemoCode = 3;
}
finally
{
Monitor.Exit(someObj);
}
事实上,不同版本之间有不同的版本
应该已经很清楚,我们可以通过以下方式来解决这一问题:
lock(someObj)
{
Monitor.Exit(someObj);
//Don't have the lock here!
Monitor.Enter(someObj);
//Have the lock again!
}
你可能想知道为什么有人会这样做,我也会想,这是一种愚蠢的方式,让代码变得不那么清晰和不那么可靠,但当你想使用
Pulse
和Wait
时,它确实起作用了,而显式Enter
和Exit
调用的版本则更清晰。就我个人而言,如果出于这个原因,我想用Pulse
或Wait
的话,我更喜欢在lock
上使用它们;我发现,lock
停止使代码更干净,并开始使其不透明。我倾向于避免这种风格,但是,正如Jon已经说过的,Monitor.Wait
释放调用它的监视器,因此在这一点上没有锁定
但这个例子有点瑕疵。问题通常是,如果在
之前调用Monitor.Pulse
,等待的线程将永远不会发出信号。考虑到这一点,作者决定“谨慎行事”,并使用了一个指定超时的重载。因此,抛开不必要的获取和释放锁,代码感觉不太正确
为了更好地解释这一点,请考虑下面的修改:
public static void tween()
{
object wait_object = new object();
Action OnComplete = () =>
{
lock (wait_object)
{
Monitor.Pulse(wait_object);
}
};
// let's say that a background thread
// finished really quickly here
OnComplete();
lock (wait_object)
{
// this will wait for a Pulse indefinitely
Monitor.Wait(wait_object);
}
}
如果在主线程中获取锁之前调用了OnComplete
,并且没有超时,我们将获得死锁。在您的情况下,Monitor.Wait
将简单地挂起一段时间,并在超时后继续,但您明白了
这就是为什么我通常推荐一种更简单的方法:
public static void tween()
{
using (AutoResetEvent evt = new AutoResetEvent(false))
{
Action OnComplete = () => evt.Set();
// let's say that a background thread
// finished really quickly here
OnComplete();
// event is properly set even in this case
evt.WaitOne();
}
}
引述:
Monitor类不保持指示已调用Pulse方法的状态。因此,如果您在没有线程等待时调用Pulse,那么调用Wait的下一个线程将阻塞,就好像从未调用过Pulse一样。如果两个线程正在使用Pulse并等待交互,这可能会导致死锁
这与AutoResetEvent类的行为形成对比:如果通过调用AutoResetEvent的Set方法向其发送信号,并且没有线程等待,则AutoResetEvent将保持信号状态,直到线程调用WaitOne、WaitAny或WaitAll。AutoResetEvent释放该线程并返回到未标记状态
+1但是,由于某种原因,我发现与使用
EventWaitHandle
进行同步相比,这种发送信号的方式稍微复杂一些。我不确定是否存在性能差异,但在这种情况下,我始终需要停止并更彻底地检查代码。如果没有其他内容,那么涉及的代码行就更多了。没有太多也没有太少的细节。我喜欢。谢谢你的理解!那篇文章非常详细地解释了Monitor
类。谢谢。因此,即使在调用lockWaitOne
之前调用了AutoResetEvent,它也会发出信号。在某些情况下,这可能很方便。唯一的问题是它没有timeout属性。在这种情况下,Monitor.Wait
中的超时是必不可少的。。这就是为什么它是首选的同步方式。最有趣的是,的MSDN示例与您的示例一样存在缺陷。:)好吧,如果是这样的话,那么为了清楚起见,我可能想切换到它。看起来它可能是一个更好、更简单、更安全的类。谢谢