从不同步的代码块调用对象同步方法。 Mutex.Release()的例外情况

我找到了关于这个例外的不同文章,但没有一个是我的情况。 这是源代码:

class Program { private static Mutex mutex; private static bool mutexIsLocked = false; static void Main(string[] args) { ICrmService crmService = new ArmenianSoftware.Crm.Common.CrmServiceWrapper(GetCrmService("Armsoft", "crmserver")); //Lock mutex for concurrent access to workflow mutex = new Mutex(true, "ArmenianSoftware.Crm.Common.FilterCtiCallLogActivity"); mutexIsLocked = true; //Create object for updating filtered cti call log ArmenianSoftware.Crm.Common.FilterCtiCallLog filterCtiCallLog = new ArmenianSoftware.Crm.Common.FilterCtiCallLog(crmService); //Bind events filterCtiCallLog.CtiCallsRetrieved += new EventHandler(filterCtiCallLog_CtiCallsRetrieved); //Execute filter try { filterCtiCallLog.CreateFilteredCtiCallLogSync(); } catch (Exception ex) { throw ex; } finally { if (mutexIsLocked) { mutexIsLocked = false; mutex.ReleaseMutex(); } } } static void filterCtiCallLog_CtiCallsRetrieved(object sender, ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs e) { tryasasas { if (mutexIsLocked) { mutexIsLocked = false; mutex.ReleaseMutex(); } } catch (Exception ex) { throw ex; } } } 

filterCtiCallLog.CreateFilteredCtiCallLogSync(); 函数执行对服务器的请求,并引发一些事件,其中一个是CtiCallsRetrieve事件。 我需要在触发此事件时释放互斥锁。 但是在调用mutex.Release()函数时会抛出exception。 CreateFilteredCtiCallLogSync同步工作。 问题是什么?

保持一个bool表示该互斥锁是一个严重的错误。 你没有让bool线程安全。 你进入这个泡菜是因为你使用了错误的同步对象。 互斥锁具有线程亲和性,互斥锁的所有者是线程。 获取它的线程也必须是调用ReleaseMutex()的线程。 这就是你的代码炸弹的原因。

您很可能需要一个事件 ,使用AutoResetEvent。 在主线程中创建它,在worker中调用Set(),在主线程中调用WaitOne()以等待worker完成其工作。 然后处理它。 另请注意,使用线程执行作业并让主线程等待其完成并不高效。 您可能还有主线程完成这项工作。

如果您实际上是为了保护对不是线程安全的对象的访问(不清楚),那么请使用lock语句。

我发现了这个问题。 关于filterCtiCallLog类的前几件事。 我设计它是为了同步和同步工作。 首先,我编写了异步执行代码。 我需要一种方法来触发从子工作线程到父项的事件,以报告工作状态。 为此,我使用了AsyncOperation类,它是post方法。 以下是触发CtiCallsRetrieved事件的代码部分。

 public class FilterCtiCallLog { private int RequestCount = 0; private AsyncOperation createCallsAsync = null; private SendOrPostCallback ctiCallsRetrievedPost; public void CreateFilteredCtiCallLogSync() { createCallsAsync = AsyncOperationManager.CreateOperation(null); ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost); CreateFilteredCtiCallLog(); } private void CreateFilteredCtiCallLog() { int count=0; //do the job //............ //........... //Raise the event createCallsAsync.Post(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count)); //........... //........... } public event EventHandler CtiCallsRetrieved; private void CtiCallsRetrievedPost(object state) { CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs; if (CtiCallsRetrieved != null) CtiCallsRetrieved(this, args); } } 

如您所见,代码正在同步执行。 这里的问题是在AsyncOperation.Post()方法中。 我假设如果在主线程中调用它,它将只是触发事件,而不是将其发布到父线程。 然而事实并非如此。 我不知道它是如何工作的,但我更改了代码,检查CreateFilteredCtiCallLog是否被称为sync或async。 如果它是异步调用我使用AsyncOperation.Post方法,如果没有,我只是触发了EventHandler如果它不是null 。 这是更正后的代码

 public class FilterCtiCallLog { private int RequestCount = 0; private AsyncOperation createCallsAsync = null; private SendOrPostCallback ctiCallsRetrievedPost; public void CreateFilteredCtiCallLogSync() { createCallsAsync = AsyncOperationManager.CreateOperation(null); ctiCallsRetrievedPost = new SendOrPostCallback(CtiCallsRetrievedPost); CreateFilteredCtiCallLog(false); } private void CreateFilteredCtiCallLog(bool isAsync) { int count=0; //do the job //............ //........... //Raise the event RaiseEvent(CtiCallsRetrievedPost, new CtiCallsRetrievedEventArgs(count),isAsync); //........... //........... } public event EventHandler CtiCallsRetrieved; private void RaiseEvent(SendOrPostCallback callback, object state, bool isAsync) { if (isAsync) createCallsAsync.Post(callback, state); else callback(state); } private void CtiCallsRetrievedPost(object state) { CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs; if (CtiCallsRetrieved != null) CtiCallsRetrieved(this, args); } } 

谢谢大家的答案!

我只有一次或两次,并且在每种情况下都是通过试图释放我没有的互斥量来实现的。

你确定事件是在获得互斥锁的同一个线程上引发的吗? 虽然你提到过filterCtiCallLog.CreateFilteredCtiCallLogSync()是一个阻塞调用,但它可能会产生引发事件的工作线程吗?

使用标志来尝试监视内核同步对象状态将无法正常工作 – 使用这些同步调用的关键是它们无需任何显式检查即可正常工作。 设置标志只会导致间歇性问题,因为标志可能会因检查标志和作用于标志之间的中断而被不适当地更改。

互斥锁只能通过获取它的威胁释放。 如果回调由另一个线程(CreateFilteredCtiCallLogSync()内部或内核线程池)调用,则发布将失败。

目前尚不清楚你究竟想做什么。 据推测,您想序列化对CreateFilteredCtiCallLogSync()的访问以及该实例可重用的回调标志吗? 如果是这样,您可以使用信号量 – init。 它到一个单元,在开始时等待它并在回调中释放它。

是否有一些问题,有时回调没有被调用,因此try / finally / release? 如果是这样,如果回调是异步的,并且在安装线程离开函数之后可能被另一个线程调用,那么这种方式似乎有点狡猾。

也许不是最有意义的错误消息,我已经看到这发生在一些第三方代码中,如下所示,

 object obj = new object(); lock (obj) { //do something Monitor.Exit(obj);//obj released }//exception happens here, when trying to release obj 

我已经看到当你使用Monitor锁定代码,然后调用异步代码并且你得到这个,当你使用锁(对象)时会遇到编译器错误,但是在monitor.enter(object)和Monitor.Exist(对象之间) )不幸的是,编译器没有抱怨。

可能发生此exception的另一个原因:

 if (Monitor.TryEnter(_lock)) { try { ... await MyMethodAsync(); ... } finally { Monitor.Exit(_lock); } } 

当’await’在另一个线程继续执行之后,我在Monitor.Exit上得到此exception。