Igo*_*lin 5 multithreading mutex thread-safety thread-synchronization
我有这个非常简单的代码,很少抛出"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();
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
Mik*_*bal 10
就我而言,我看到了与内森·舒布克格尔 (Nathan Schubkegel) 相同的行为。我使用await's,并Thread.CurrentThread.ManagedThreadId为“相同”线程提供另一个值。我的意思是,线程是用 启动的ManagedThreadId == 10,并由Mutex该线程 id 拥有,但后来ReleaseMutex()导致ApplicationException消息:“对象同步方法是从不同步的代码块调用的”,我ManagedThreadId == 11此时看到了:)。看起来,await有时返回时会更改线程ID。看来,就是这个原因。Mutex认为另一个线程想要释放它。遗憾的是,文档此时Mutex并未引起注意。
因此,您不能await在获取和释放之间使用异步运算符Mutex。这是因为 C# 编译器用异步回调替换了普通运算符,并且该回调可以由另一个await线程进行。通常,它是同一个线程,但有时它是另一个线程(来自线程池)。
Mutex检查线程。只有获取的线程才Mutex可以释放它。如果您需要同步而不进行此检查,请使用Semaphore。SemaphoreSlim有异步方法WaitAsync()- 很酷。
catch (AbandonedMutexException)
{
acquiredMutex = true;
}
Run Code Online (Sandbox Code Playgroud)
这是代码中一个非常严重的错误.捕获AbandonedMutexException永远不正确,这是一个非常严重的事故.另一个线程获得了互斥锁,但在没有调用ReleaseMutex()的情况下终止了.您已无法恢复同步,并且互斥锁不再可用.
你犯了一个错误并且假设你无论如何都获得了互斥体,你有点幸运.你没有.现在,ReleaseMutex()调用将以你引用的异常进行轰炸.
除了通过终止程序(明智的选择)或完全禁用日志记录之外,您无法从此次事故中恢复,因此永远不会再次使用互斥锁.通过删除catch子句做出明智的选择.发现问题的真正根源,那个崩溃并且没有调用ReleaseMutex()的线程,对于这个问题已经脱离了上下文,没有提示.你一直忽略了这个问题,通过捕捉AME来掩盖它,你不能忽视它.
ReleaseMutex()当您从不拥有互斥体的线程调用时,会引发此异常。搜索// some application code here释放互斥锁的代码。
还要重新考虑您是否实际上是ReleaseMutex()从调用的同一线程进行调用WaitOne()。示例:我到达这篇文章是因为我正在使用async/await并且我的代码在不同的线程上恢复并尝试释放该线程不拥有的互斥锁。