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

时间:2012-01-26 11:31:38

标签: c# .net multithreading mutex

我找到了关于此异常的不同文章,但这些都不是我的情况。 这是源代码:

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同步工作。有什么问题?

8 个答案:

答案 0 :(得分:40)

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

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

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

答案 1 :(得分:5)

我发现了这个问题。关于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);
    }
}

感谢大家的答案!

答案 2 :(得分:3)

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

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

那是因为等待之后的代码可以在与之前的代码不同的线程上执行。基本上,似乎如果现在(在2020年初)增强代码,则互斥对象根本无法工作。使用事件或其他东西。

答案 3 :(得分:2)

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

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

答案 4 :(得分:2)

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

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

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

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

答案 5 :(得分:2)

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

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

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

object obj = new object();
lock (obj)
{
    //do something

    Monitor.Exit(obj);//obj released

}//exception happens here, when trying to release obj