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

kyu*_*yan 24 .net c# 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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Han*_*ant 42

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

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

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

  • 这是不正确的,互斥锁始终是线程仿射的。命名它允许跨进程边界使用互斥锁。这让一个废弃的互斥锁变得更糟,告诉你进程意外终止。 (2认同)

Igo*_*pov 12

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

if (Monitor.TryEnter(_lock))
{
    try
    {
        ... await MyMethodAsync(); ...
    }
    finally
    {
        Monitor.Exit(_lock);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 看到这个更好的方法,为什么你不应该这样做:/sf/ask/532882171/锁定语句 (3认同)

kyu*_*yan 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,代码正在同步执行.这里的问题在于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);
    }
}
Run Code Online (Sandbox Code Playgroud)

谢谢大家的答案!


小智 5

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

        mutex.WaitOne();
        … Some Work...
        await someTask;
        mutex.ReleaseMutex();
Run Code Online (Sandbox Code Playgroud)

那是因为 await 之后的代码可以在与之前的行不同的线程上执行。基本上,如果你现在(在 2020 年初)异步代码,互斥锁根本不起作用。使用事件什么的。