【问题标题】:Monitor.Exit throws SynchronizationLockExceptionMonitor.Exit 抛出 SynchronizationLockException
【发布时间】:2014-12-18 12:55:15
【问题描述】:

所以,我已经收到这个错误一段时间了,我进行了一些测试,但我无法找出问题所在。调用 Monitor.Exit() 时出现 System.Threading.SynchronizationException。首先,我为我使用的所有 Monitor 方法创建了一个包装器,将“Locked”和“Unlocked”打印到屏幕上。这在我调用 Exit 之前打印得很好,它说锁已锁定。当我调用 exit 时,它会引发错误并且无法解锁我的对象。这在我的 UI 中是显而易见的。它说不能从非同步块执行?

注意:我的 Monitor.Exit 命令是在不同的方法调用中调用的,然后是我的 Monitor.Enter 命令。它仍然是安全的,但这可能是问题吗?

编辑:背景信息:我正在实现一个类似马里奥的游戏。当马里奥进入过渡期时,我的团队希望我们所有的各种计时器停止更新(阻止他们的线程)并且只对我们的玩家进行特定的更新。所有 Timer 线程都已经使用了一个名为 TimerLock 的公用锁。这是此转换的示例代码。

    public void Begin()
    {
        if (Monitor.TryEnter(ManagedTimer.Lock,100))
        {

            try
            {
                //Turn off updating for all objects
                TurnOffPhysicsUpdate(); //Psuedocode for this...

                //Create timer till end event
                EndTimer = new System.Timers.Timer(600.00);
                EndTimer.AutoReset = false;
                EndTimer.Elapsed += EndTimer_Elapsed;
                EndTimer.Enabled = true;
                EndTimer.Start();

                //Create swap timer
                SwapTimer = new System.Timers.Timer(25.0);
                SwapTimer.AutoReset = false;
                SwapTimer.Elapsed += SwapTimer_Elapsed;
                SwapTimer.Enabled = true;
                SwapTimer.Start();
            }
            catch (Exception e)
            {
                EndFreeze();
                Debug.WriteLine("Failed to setup transition: " + e.Message);
            }
        }
    }

    private void SwapTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
            SwapTimer.Stop();
            //Transition Mario State....
            SwapTimer.Start();
    }

    private void EndTimer_Elapsed(object sender, ElapsedEventArgs e)
    {
        EndFreeze();
    }

    private void EndFreeze()
    {
        try
        {
            SwapTimer.Stop();
            EndTimer.Stop();

            TurnOnPhysicsUpdate(); //psuedocode

            //Ensure Mario ends in the proper state
            user.StateMachine.TransitionSizeState(newState);
        }
        catch (Exception e)
        {
            Debug.WriteLine("FATAL ERROR: " + e.Message);
        }
        finally
        {
            //Exception occurs here!!!!!!!!!!!!!
            Monitor.Exit(ManagedTimer.Lock,"TimerLock");
        }
    }

【问题讨论】:

  • 听起来你的进入和退出调用不同步,如果你能原谅双关语的话。
  • 退出时你在正确的线程上吗?它必须从进入的同一线程中退出
  • 当您说不同步时,您是什么意思?我知道该对象在我调用时被锁定。我所能想到的是,它们不是从同一个方法调用的事实......
  • 这个问题很模糊。显示一些示例代码可能会有所帮助。脑海中浮现的一个明显问题是,“你真正想要解决代码中的什么条件?” -- 附加信息,例如,这是一个 Windows 窗体应用程序吗?将有助于我们更好地理解问题。
  • 转换工作正常,但是当它退出时,Monitor.Exit 抛出异常并且不释放......

标签: c# multithreading exception thread-safety monitor


【解决方案1】:

当你的EndTimer_Elapsed方法被定时器调用时,你不在拥有锁的同一个线程中,所以你不能在那里释放锁。

这是一个很好的例子,说明了为什么应该完全避免使用Monitor.Enter/Exit。即使是并发编程方面的专家也很难让一切都恰到好处,对于其他人来说,这也是一个雷区。如果您坚持通过 lock 语句使用 Monitor,那么犯这种错误会变得更加困难。 :)

最后请注意,从您的示例中并不清楚为什么您要使用此锁。您可能会考虑发布一个不同的问题来解释您尝试解决的同步问题,以便您可以在没有Monitor.Enter/Exit

的情况下获得正确设计代码的帮助

【讨论】:

  • 感谢您的帮助。我的计时器线程调用 Exit,我的主线程调用 Enter。这是有道理的,但我不太确定如何解决这个问题而不会在代码库中造成巨大的裂痕......我不能再发布一个半小时......所以如果你有任何提示,我将不胜感激它
  • 我确信我和其他人可以提供建议,但并非没有更多关于您为什么要使用此锁的信息。保持如此长时间的锁定是不寻常的,并且在这里可能是不明智的。 (我知道 25 甚至 600 毫秒似乎是一个很短的计时器,但对于现代计算机来说它是永恒的)
  • 好的,我可以更详细地介绍一个项目,很难用简短的简介来总结一个项目。我们有马里奥,目标是马里奥的过渡。精灵和其他一些组件(用于解耦)知道如何通过计时器激活和更新自己。我们都在上学,并没有在线程方面做太多事情,所以我们决定在所有计时器周围设置一个锁,以确保没有两个线程相互运行。 (这种游戏的速度损失似乎没有任何影响)。现在要转换,停止所有这些计时器的最简单方法似乎是让转换方法锁定。
  • 我知道这很难概括。这就是为什么 a) 这种类型的论坛不适合解决较大的设计相关问题,并且 b) 如果您确实需要这些问题的帮助,您需要将重要元素提炼成一个简洁但完整的代码示例可靠地证明了问题 (stackoverflow.com/help/mcve)
  • 所以我创建了上面的代码来获取锁,来回执行几次状态交换,最终在更长的时间后释放锁,允许所有项目继续。我知道这浪费了多线程的优势,但遗憾的是,直到后来我们才知道定时器可以使用多线程。 掌心
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-02-19
  • 2012-06-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多