2012-01-26 54 views
17

He encontrado diferentes artículos sobre esta excepción, pero ninguno de ellos era mi caso. Aquí está el código fuente:Se ha llamado al método de sincronización de objetos desde un bloque de código no sincronizado. Excepción en 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(); función ejecuta las peticiones al servidor, y plantea algunos eventos, uno de los cuales es CtiCallsRetrieve evento. Y necesito liberar el mutex cuando se active este evento. Pero al llamar se lanza la excepción de función mutex.Release(). CreateFilteredCtiCallLogSync funciona sincrónicamente. ¿Cuál es el problema?

Respuesta

33

Mantener un bool alrededor que indica que el mutex es propiedad es un grave error. No estás haciendo que el bool sea seguro para los hilos. Te metiste en este encurtido porque estás usando el objeto de sincronización incorrecto. Un mutex tiene afinidad de hilos, el propietario de un mutex es un hilo. El hilo que lo adquirió también debe ser el que llame a ReleaseMutex(). Por eso tu código es una bomba.

Es muy probable que necesite un evento aquí, use AutoResetEvent. Créelo en el hilo principal, llame a Set() en el trabajador, WaitOne() en el hilo principal para esperar que el trabajador complete su trabajo. Y deséchelo después. También tenga en cuenta que el uso de un hilo para realizar un trabajo y que su hilo principal espere hasta su finalización no es productivo. Es mejor que el hilo principal haga el trabajo.

Si realmente está haciendo esto para proteger el acceso a un objeto que no es seguro para subprocesos (no está claro), utilice la instrucción de bloqueo.

+0

Tienes razón, me perdí que el hilo que configuraba la devolución de llamada es el hilo principal :( –

+0

AutoResetEvent es genial! Gracias Hans. –

2

Solo tuve esta una o dos veces, y en todos los casos se trató de intentar liberar un mutex que no era de mi propiedad.

¿Estás seguro de que los eventos se producen en el mismo hilo en el que se adquirió el mutex? Aunque mencionas que filterCtiCallLog.CreateFilteredCtiCallLogSync() es una llamada de bloqueo, ¿tal vez genera hilos de trabajo que provocan el evento?

2

El uso de un indicador para intentar controlar un estado del objeto sincronizado kernel simplemente no funcionará; el punto de utilizar esas llamadas de sincronización es que funcionan correctamente sin ninguna comprobación explícita. Los indicadores de configuración solo causarán problemas intermitentes porque la bandera se puede cambiar de manera inapropiada debido a las interrupciones entre verificar la bandera y actuar sobre ella.

Un mutex solo puede ser liberado por la amenaza que lo adquirió. Si se llama a una devolución de llamada por un hilo diferente, (uno interno a CreateFilteredCtiCallLogSync() o un grupo de hilos del núcleo), la versión fallará.

No está claro exactamente lo que está intentando hacer. Presumiblemente, ¿desea serializar el acceso a CreateFilteredCtiCallLogSync() y los indicadores de devolución de llamada que la instancia está disponible para su reutilización? Si es así, podrías usar un semáforo en su lugar: init. a una unidad, espere al comienzo y suéltelo en la devolución de llamada.

¿Hay algún problema donde a veces no se llama a la devolución de llamada y, por lo tanto, la versión try/finally/release? Si es así, esta salida parece un poco dudosa si la devolución de llamada es asíncrona y puede ser llamada por otro hilo después de que el hilo de configuración haya salido de la función.

5

He encontrado el problema. Primero varias cosas sobre la clase filterCtiCallLog. Lo he diseñado para que funcione tanto asíncrono como sincrónico. Para empezar, he escrito el código para la ejecución asincrónica. Necesitaba una forma de desencadenar eventos desde el hilo del trabajador infantil al padre, para informar el estado de trabajo. Para esto he usado la clase AsyncOperation y su método de publicación. Aquí está la parte del código para activar el evento 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); 
    } 
} 

Como se puede ver el código se ejecuta de forma sincrónica. El problema aquí es en el método AsyncOperation.Post(). Supuse que si se llama en el hilo principal, actuará simplemente como desencadenar el evento, no publicarlo en el hilo padre. Sin embargo, no fue el caso. No sé cómo está funcionando, pero he cambiado el código para verificar si el CreateFilteredCtiCallLog se llama sincronización o asincronía. Y si es una llamada asincrónica utilicé el método AsyncOperation.Post, si no, simplemente activé el EventHandler si no es null. Aquí está el código corregido

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

¡Gracias a todos por las respuestas!

0

Quizás no sea el mensaje de error más significativo, he visto que esto suceda en algún código de terceros como abajo,

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

    Monitor.Exit(obj);//obj released 

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

he visto que esto suceda cuando se bloquea el código utilizando un monitor, a continuación, llamar a un asíncrono código y obtienes esto, cuando usas un bloqueo (objeto) obtienes un error de compilación, sin embargo entre monitor.enter (objeto) y Monitor.Exist (objeto) el compilador no se queja ... desafortunadamente.

Cuestiones relacionadas