ReleaseMutex :对象同步方法是从未同步的代码块中调用的

Posted

技术标签:

【中文标题】ReleaseMutex :对象同步方法是从未同步的代码块中调用的【英文标题】:ReleaseMutex : Object synchronization method was called from an unsynchronized block of code 【发布时间】: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();
                
            
        
    

【问题讨论】:

【参考方案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:“请求互斥锁所有权的下一个线程可以处理此异常并继续进行,前提是可以验证数据结构的完整性。”并且:***.com/questions/15456986/… 没有圣诞老人,除非你能证明雪橇真的可以在空中飞翔。这确实需要先让雪橇飞起来。编写一个有意放弃互斥锁并保持程序运行的测试。

以上是关于ReleaseMutex :对象同步方法是从未同步的代码块中调用的的主要内容,如果未能解决你的问题,请参考以下文章

使用 Mutex 同步 C# 对象:在 C# 对象析构函数中调用 ReleaseMutex() 时出现问题

SynchronizationLockException(对象同步方法是从未同步的代码块中调用的。)释放锁时

对象同步方法是从未同步的代码块中调用的。 Mutex.Release() 上的异常

window下线程同步之(Mutex(互斥器) )

互斥体

转载5天不再惧怕多线程——第三天 互斥体