Метод синхронизации объекта был вызван из несинхронизированного блока кода. Исключение на Mutex.Release () - PullRequest
22 голосов
/ 26 января 2012

Я нашел разные статьи об этом исключении, но ни одна из них не была моим случаем.Вот исходный код:

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 работает синхронно.В чем проблема?

Ответы [ 7 ]

40 голосов
/ 26 января 2012

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

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

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

5 голосов
/ 30 января 2012

Я нашел проблему.Сначала несколько вещей о классе 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 голосов
/ 26 января 2012

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

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

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

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

2 голосов
/ 12 октября 2018

Другая причина, по которой может возникать это исключение:

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

Я получаю это исключение на Monitor.Exit, когда после 'await' другой поток продолжает выполнение.

2 голосов
/ 26 января 2012

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

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

1 голос
/ 01 февраля 2017

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

0 голосов
/ 17 ноября 2016

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

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

    Monitor.Exit(obj);//obj released

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