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
的线程才能释放它。如果您需要不进行此检查的同步,请使用Semaphore
。 SemaphoreSlim
有异步方法 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(对象同步方法是从未同步的代码块中调用的。)释放锁时