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

Posted

技术标签:

【中文标题】对象同步方法是从未同步的代码块中调用的。 Mutex.Release() 上的异常【英文标题】:Object synchronization method was called from an unsynchronized block of code. Exception on Mutex.Release() 【发布时间】:2012-02-19 12:16:05 【问题描述】:

我找到了有关此异常的不同文章,但没有一篇是我的情况。 以下是源代码:

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<ArmenianSoftware.Crm.Common.CtiCallsRetrievedEventArgs>(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() 函数时会抛出异常。 CreateFilteredCtiCallLogSync 同步工作。有什么问题?

【问题讨论】:

【参考方案1】:

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

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

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

【讨论】:

你是对的 - 我错过了设置回调的线程是主线程:( 已命名有问题的互斥锁。从 Microsoft 文档来看,这意味着它没有线程亲和性。我不是这方面的专家。实际上,我自己也遇到了同样的错误,需要帮助。我将提出一个单独的问题来设置场景。 这是不正确的,互斥锁总是线程仿射的。命名它允许跨进程边界使用互斥锁。这使废弃的互斥锁变得更糟,告诉您进程意外终止。【参考方案2】:

可能出现此异常的另一个原因:

if (Monitor.TryEnter(_lock))

    try
    
        ... await MyMethodAsync(); ...
    
    finally
    
        Monitor.Exit(_lock);
    

当“等待”后另一个线程继续执行时,我在 Monitor.Exit 上收到此异常。

编辑: 使用 SemaphoreSlim,因为它不需要释放线程相同。

【讨论】:

查看这个以获得更好的方法以及为什么不应该这样做:***.com/questions/7612602/…【参考方案3】:

如果您执行以下操作,您也会遇到此异常:

        mutex.WaitOne();
        … Some Work...
        await someTask;
        mutex.ReleaseMutex();

这是因为 await 之后的代码可以在与前一行不同的线程上执行。基本上,如果您现在(2020 年初)异步代码,互斥锁似乎根本不起作用。使用事件或其他东西。

【讨论】:

【参考方案4】:

我发现了问题。首先是关于 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<CtiCallsRetrievedEventArgs> CtiCallsRetrieved;

    private void CtiCallsRetrievedPost(object state)
    
        CtiCallsRetrievedEventArgs args = state as CtiCallsRetrievedEventArgs;
        if (CtiCallsRetrieved != null)
            CtiCallsRetrieved(this, args);
    

如您所见,代码正在同步执行。这里的问题在于AsyncOperation.Post() 方法。我假设如果在主线程中调用它,它将充当简单的触发事件,而不是将其发布到父线程。然而事实并非如此。我不知道它是如何工作的,但我已经更改了代码,以检查 CreateFilteredCtiCallLog 是称为同步还是异步。如果是异步调用,我使用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<CtiCallsRetrievedEventArgs> 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);
    

感谢大家的回答!

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

使用标志来尝试监控内核同步对象状态是行不通的——使用这些同步调用的重点是它们无需任何显式检查即可正常工作。设置标志只会导致间歇性问题,因为标志可能会由于检查标志和对其进行操作之间的中断而被不当更改。

互斥锁只能由获得它的威胁释放。如果您的回调被不同的线程(CreateFilteredCtiCallLogSync() 或内核线程池内部的线程)调用,则发布将失败。

目前尚不清楚您正在尝试做什么。大概,您想序列化对 CreateFilteredCtiCallLogSync() 的访问以及实例可重用的回调标志?如果是这样,您可以改用信号量 - init。它到一个单元,在开始时等待它并在回调中释放它。

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

【讨论】:

【参考方案7】:

我只有一次或两次,而且每次都是通过尝试释放我不拥有的互斥锁来实现的。

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

【讨论】:

【参考方案8】:

也许不是最有意义的错误消息,我在下面的一些第三方代码中看到过这种情况,

object obj = new object();
lock (obj)

    //do something

    Monitor.Exit(obj);//obj released

//exception happens here, when trying to release obj

【讨论】:

很好的例子 :) 我在将鼠标拖回锁中时发生了这种情况。可能是 EditAndContinue 的一些错误。

以上是关于对象同步方法是从未同步的代码块中调用的。 Mutex.Release() 上的异常的主要内容,如果未能解决你的问题,请参考以下文章

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

为什么wait(),notify(),notifyAll()必须在同步方法/代码块中调用?

为什么 wait(), notify()和 notifyAll ()必须在同步方法或 者同步块中被调用?

为什么 wait(), notify()和 notifyAll ()必须在同步方法或 者同步块中被调用?

为什么wait和notify方法要在同步块中调用?

ReleaseMutex:从非同步代码块调用对象同步方法