La méthode de synchronisation des objets a été appelée à partir d'un bloc de code non synchronisé. Exception sur Mutex.Publier()

j'ai trouvé différents articles sur cette exception mais aucun d'eux n'était mon cas. Voici le code source:

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(); la fonction exécute les requêtes vers le serveur, et génère certains événements, dont l'un est CtiCallsRetrieve événement. Et je dois libérer le mutex quand cet événement est déclenché. Mais en appelant le mutex.L'exception de la fonction Release() est lancée. CreateFilteredCtiCallLogSync fonctionne de façon synchrone. Quel est le problème?

19
demandé sur Amar Palsapure 2012-01-26 15:31:38

6 réponses

bool autour qui indique que le mutex est la propriété est une grave erreur. Tu ne rends pas le fil bool-safe. Vous avez dans ce cornichon parce que vous utilisez le mauvais objet de synchronisation. Un mutex a une affinité fil, le propriétaire d'un mutex est un fil. Le fil qui l'a acquis doit aussi être celui qui appelle ReleaseMutex (). C'est pour ça que votre code est nul.

selon toute vraisemblance, vous avez besoin d'un événement ici, utilisez AutoResetEvent. Créer dans la thread principal de l'appel() dans le travailleur, WaitOne() dans le thread principal pour attendre le travailleur à remplir son emploi. Et en disposer par la suite. Notez également que l'utilisation d'un thread pour effectuer un travail et d'avoir votre thread principal d'attendre son achèvement n'est pas productif. Vous pourriez aussi bien avoir le fil principal faire le travail.

si vous faites cela pour protéger l'accès à un objet qui n'est pas thread-safe (ce n'est pas clair) alors utilisez le verrouillage déclaration.

36
répondu Hans Passant 2012-01-26 11:50:36

j'ai trouvé le problème. Tout d'abord, plusieurs choses à propos de la classe filterCtiCallLog. Je l'ai conçu pour fonctionner à la fois asynchrone et synchrone. Pour la première fois j'ai écrit le code pour l'exécution asynchrone. J'avais besoin d'un moyen de déclencher des événements depuis le fil enfant travailleur vers le parent, pour signaler l'état de travail. Pour cela, j'ai utilisé Asyncopération la classe et c'est la méthode de la poste. Voici la partie de code pour déclencher L'événement 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);
    }
}

Comme vous pouvez le voir le code s'exécute de façon synchrone. Ici, le problème est dans AsyncOperation.Post() méthode. J'ai supposé que s'il est appelé dans le thread principal, il agira simplement comme déclenchant l'événement, et non pas en le postant dans le thread parent. Cependant, il n'était pas le cas. Je ne sais pas comment il fonctionne, mais j'ai changé le code pour vérifier si le CreateFilteredCtiCallLog s'appelle sync ou async. Et si c'est l'appel asynchrone j'ai utilisé AsyncOperation.Post méthode, si non, j'ai simplement déclenché l' EventHandler si il n'est pas null. Voici le corrigé code

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);
    }
}

Merci à tous pour les réponses!

5
répondu kyurkchyan 2012-01-30 05:28:31

Je n'ai eu celui-ci qu'une ou deux fois, et dans tous les cas il est venu en essayant de libérer un mutex Je ne possédais pas.

Êtes-vous sûr que les événements sont soulevés sur le même fil que le mutex a été acquis sur? Bien que vous le mentionniez!--0--> est un appel de blocage, peut-être qu'il engendre des threads ouvriers qui soulèvent l'événement?

2
répondu Willem van Rumpt 2012-01-26 11:47:27

utiliser un drapeau pour essayer de surveiller l'état d'un objet synchro du noyau ne fonctionnera tout simplement pas - le point d'utiliser ces appels synchro est qu'ils fonctionnent correctement sans aucune vérification explicite. Le réglage des drapeaux ne fera que causer des problèmes intermittents parce que le drapeau peut être modifié de façon inappropriée en raison d'interruptions entre la vérification du drapeau et l'action sur celui-ci.

Un mutex peut être libérée par la menace qui l'a acquis. Si vous rappelez est appelé par un fil différent, (un interne pour CreateFilteredCtiCallLogSync () ou un pool de threads du noyau), la version échouera.

Il n'est pas clair exactement ce que vous tentez de faire. Probablement, vous voulez sérialiser l'accès à CreateFilteredCtiCallLogSync () et les options de callback que l'instance est disponible pour la réutilisation? Si c'est le cas, vous pouvez utiliser un sémaphore à la place - init. il à une unité, l'attendre au début et le relâcher dans le rappel.

Est-il un problème où, parfois, le rappel n'est pas appelé, et donc de l'essayer/enfin/release? Si c'est le cas, cette sortie semble un peu douteuse si le rappel est asychronous et peut être appelé par un autre thread après que le thread setup a quitté la fonction.

2
répondu Martin James 2012-01-26 12:00:59

peut-être pas le message d'erreur le plus significatif, j'ai vu cela se produire dans un code tiers comme ci-dessous,

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

    Monitor.Exit(obj);//obj released

}//exception happens here, when trying to release obj
0
répondu Nemo 2016-11-17 20:07:29

j'ai vu cela se produire quand vous verrouillez le code en utilisant un moniteur, puis appelez un code async et vous obtenez ceci, quand vous utilisez un lock(object) vous obtenez une erreur de compilateur, cependant entre les moniteurs.entrer (objet) et surveiller.Existe (objet) le compilateur ne se plaint pas... malheureusement.

0
répondu Walter Verhoeven 2017-02-01 15:49:58