Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 关于获取锁的说明_C#_.net_Multithreading_Locking - Fatal编程技术网

C# 关于获取锁的说明

C# 关于获取锁的说明,c#,.net,multithreading,locking,C#,.net,Multithreading,Locking,我已经用C#编码了很长一段时间,但是这个锁定序列对我来说没有任何意义。我对锁定的理解是,一旦通过lock(object)获得锁,代码必须退出锁作用域才能解锁对象 这就引出了我手头的问题。我删掉了下面的代码,它恰好出现在我的代码中的动画类中。该方法的工作方式是将设置传递给该方法并进行修改,然后传递给另一个重载方法。另一个重载方法将把所有信息传递给另一个线程,以便以某种方式处理并实际设置对象的动画。动画完成后,另一个线程调用OnComplete方法。实际上,这一切都非常有效,但我不明白为什么 另一个

我已经用C#编码了很长一段时间,但是这个锁定序列对我来说没有任何意义。我对锁定的理解是,一旦通过
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
类。谢谢。因此,即使在调用lock
WaitOne
之前调用了AutoResetEvent,它也会发出信号。在某些情况下,这可能很方便。唯一的问题是它没有timeout属性。在这种情况下,
Monitor.Wait
中的超时是必不可少的。。这就是为什么它是首选的同步方式。最有趣的是,的MSDN示例与您的示例一样存在缺陷。:)好吧,如果是这样的话,那么为了清楚起见,我可能想切换到它。看起来它可能是一个更好、更简单、更安全的类。谢谢