【问题标题】:ReleaseMutex : Object synchronization method was called from an unsynchronized block of codeReleaseMutex :对象同步方法是从未同步的代码块中调用的
【发布时间】:2015-02-06 17:02:01
【问题描述】:

我有这段非常简单的代码,它很少抛出“System.ApplicationException:对象同步方法是从未同步的代码块中调用的”。当 ReleaseMutex() 被调用时。

我从逻辑上分析了方法的流程,但无法理解这是如何/为什么会发生的。 据我了解,在这种情况下,互斥锁的所有权得到了保证:

    readonly string mutexKey;

    public Logger(string dbServer, string dbName)
    {
        this.mutexKey = ServiceManagerHelper.GetServiceName(dbServer, dbName);
    }

    private void Log(LogType type, string message, Exception ex)
    {
        using (var mutex = new Mutex(false, mutexKey))
        {
            bool acquiredMutex;
            try
            {
                acquiredMutex = mutex.WaitOne(TimeSpan.FromSeconds(5));
            }
            catch (AbandonedMutexException)
            {
                acquiredMutex = true;
            }

            if (acquiredMutex)
            {
                try
                {

                    // some application code here

                }
                finally
                {
                    mutex.ReleaseMutex();
                }
            }
        }
    }

【问题讨论】:

    标签: multithreading thread-safety mutex thread-synchronization


    【解决方案1】:

    就我而言,我看到了像 Nathan Schubkegel 一样的行为。我使用await,而Thread.CurrentThread.ManagedThreadId 为“相同”线程提供了另一个值。我的意思是,线程以ManagedThreadId == 10 启动,而Mutex 拥有此线程ID,但后来ReleaseMutex() 导致ApplicationException 出现消息:“从未同步的代码块调用对象同步方法”,而我此时看到ManagedThreadId == 11 :) 。看来,await 在返回时有时会更改线程 ID。看来,就是这个原因。 Mutex 认为 另一个 线程想要释放它。很遗憾,Mutex 文档在这一刻没有引起注意

    所以,您不能Mutex 获取和释放之间使用异步运算符 await。这是因为C#编译器用异步回调代替了普通操作符await,而这个回调可以由ANOTHER线程进行。通常,它是同一个线程,但有时它是另一个线程(来自线程池)。

    Mutex 检查线程。只有获得Mutex 的线程才能释放它。如果您需要不进行此检查的同步,请使用SemaphoreSemaphoreSlim 有异步方法 WaitAsync() - 很酷。

    【讨论】:

      【解决方案2】:

      当您从不拥有互斥锁的线程调用ReleaseMutex() 时会引发此异常。在// some application code here 中搜索释放互斥锁的代码。

      还要重新考虑您是否实际上是从您调用WaitOne() 的同一线程调用ReleaseMutex()。示例:我看到这篇文章是因为我使用了async/await,并且我的代码在另一个线程上恢复并试图释放线程不拥有的互斥锁。

      【讨论】:

      • 它是一个命名互斥体,所以我不认为你在这里是正确的。
      • “因为我使用 async/await 并且我的代码在不同的线程上恢复并试图释放线程不拥有的互斥锁” - 当您使用 await xx.ConfigureAwait 时就会出现这种情况(错误的)。命名互斥体也会引发此异常。
      • 这个答案是正确的。拥有 Mutex 的线程通常与等待异步代码后等待完成后恢复的线程不同。
      【解决方案3】:
              catch (AbandonedMutexException)
              {
                  acquiredMutex = true;
              }
      

      这是您代码中的一个非常严重的错误。捕获 AbandonedMutexException 永远不会正确,这是一个非常严重的事故。另一个线程获取了互斥体,但没有调用 ReleaseMutex() 就终止了。您已经不可恢复地失去了同步,并且互斥锁不再可用。

      您犯了一个错误并假设您无论如何都获得了互斥锁,这有点幸运。你没有。 ReleaseMutex() 调用现在将与您引用的异常一起爆炸。

      除了终止程序(明智的选择)或完全禁用日志记录以便永远不会再次使用互斥锁之外,您无法从这种不幸中恢复。通过删除 catch 子句做出明智的选择。发现问题的真正根源,即崩溃且未调用 ReleaseMutex() 的线程与此问题无关,没有任何提示。你一直忽略这个问题,通过抓AME来掩盖它,你不能忽略它。

      【讨论】:

      • 嗨,汉斯,感谢您的回复。虽然我还是不明白。。MSDN 说:“当一个线程放弃一个互斥锁时,在下一个获取互斥锁的线程中抛出异常。”因此,如果我们处理这个异常,我们可以验证受保护实体的正确性——我们很好。我们不是吗?如果不是,为什么?同样来自 MSDN:“请求互斥锁所有权的下一个线程可以处理此异常并继续进行,前提是可以验证数据结构的完整性。”并且:stackoverflow.com/questions/15456986/…
      • 没有圣诞老人,除非你能证明雪橇真的可以在空中飞翔。这确实需要先让雪橇飞起来。编写一个有意放弃互斥锁并保持程序运行的测试。
      猜你喜欢
      • 2011-11-28
      • 1970-01-01
      • 2012-09-14
      • 2012-02-19
      • 2012-12-09
      • 1970-01-01
      • 2013-01-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多