Метод синхронизации объектов вызывается из несинхронизированного блока кода. Исключение из Mutex.Release() - программирование
Подтвердить что ты не робот

Метод синхронизации объектов вызывается из несинхронизированного блока кода. Исключение из 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<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 работает синхронно. В чем проблема?

4b9b3361

Ответ 1

Сохранение bool вокруг означает, что мьютекс принадлежит, является серьезной ошибкой. Вы не делаете bool потокобезопасным. Вы попали в этот рассол, потому что используете неправильный объект синхронизации. Мьютекс имеет нить-сродство, владелец мьютекса - это поток. Потоком, который его приобрел, также должен быть тот, который вызывает ReleaseMutex(). Вот почему ваш код бомбит.

Вам, по всей вероятности, нужно событие здесь, используйте AutoResetEvent. Создайте его в основном потоке, вызовите Set() в рабочем, WaitOne() в основном потоке, чтобы дождаться, пока работник выполнит свое задание. И распоряжаться им потом. Также обратите внимание, что использование потока для выполнения задания и наличие основного потока для его завершения не является продуктивным. Вы можете также иметь основной поток выполнить эту работу.

Если вы на самом деле делаете это для защиты доступа к объекту, который не является потокобезопасным (это неясно), используйте оператор блокировки.

Ответ 2

Я нашел проблему. Сначала несколько вещей о классе 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);
    }
}

Спасибо всем за ответы!

Ответ 3

У меня был только один или два раза, и в каждом случае это произошло, пытаясь освободить мьютекс, которого я не имел.

Вы уверены, что события подняты в том же потоке, на котором был принят мьютекс? Хотя вы отмечаете, что filterCtiCallLog.CreateFilteredCtiCallLogSync() является блокирующим вызовом, возможно, он порождает рабочие потоки, которые поднимают событие?

Ответ 4

Использование флага для попытки контролировать состояние синхронизируемого объекта ядра просто не сработает - точка использования этих синхронных вызовов заключается в том, что они работают корректно без какой-либо явной проверки. Флаги установки просто вызовут прерывистые проблемы, потому что флаг может быть изменен ненадлежащим образом из-за прерываний между проверкой флага и действием на него.

Мьютекс может быть освобожден только той угрозой, которую он приобрел. Если обратный вызов вызывается другим потоком (один внутренний для CreateFilteredCtiCallLogSync() или пул потоков ядра), релиз завершится неудачно.

Не ясно, что именно вы пытаетесь сделать. Предположительно, вы хотите сериализовать доступ к CreateFilteredCtiCallLogSync() и флаги обратного вызова, которые экземпляр доступен для повторного использования? Если это так, вы можете использовать семафор вместо - init. до одного устройства, дождитесь его в начале и отпустите в обратном вызове.

Есть ли проблема, когда иногда callback не вызывается, и, следовательно, try/finally/release? Если так, этот способ кажется немного хитростью, если обратный вызов является асинхронным и может быть вызван другим потоком после того, как поток настройки оставил эту функцию.

Ответ 5

Возможно, это не самое содержательное сообщение об ошибке, я видел, как это происходит в коде третьей стороны, как показано ниже,

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

    Monitor.Exit(obj);//obj released

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

Ответ 6

Я видел это, когда вы блокируете код с помощью монитора, а затем вызываете асинхронный код, и вы получаете это, при использовании блокировки (объекта) вы получаете ошибку компилятора, однако между monitor.enter(object) и Monitor. Exist (объект) компилятор не жалуется... к сожалению.