2013-06-12 22 views
5

Ho un'applicazione Web che deve utilizzare una cache dell'applicazione per archiviare i dati (a causa dell'elevato sovraccarico di ottenere tali dati su una richiesta per richiesta). Vedi post precedente a https://stackoverflow.com/a/16961962/236860Rilascio di un mutex

Questo approccio sembra funzionare bene, ma sto vedendo i seguenti errori occasionali errori del sito web:

System.ApplicationException: Object synchronization method was called from an 
unsynchronized block of code. 
at System.Threading.Mutex.ReleaseMutex() 
at InboxInsight.Web.Web_Controls.Twitter.TwitterFeed.GetTwitterData(HttpContext context) 
at InboxInsight.Web.Web_Controls.Twitter.TwitterFeed.ProcessRequest(HttpContext context) 
at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) 

Per riferimento, ecco il blocco di codice:

public string GetData(HttpContext context) 
{ 
    var cache  = context.Cache; 
    Mutex mutex = null; 
    string data = (string)cache[CacheKey]; 

    // Start check to see if available on cache 
    if (data == null) 
    { 
     try 
     { 
      // Lock base on resource key 
      // (note that not all chars are valid for name) 
      mutex = new Mutex(true, CacheKey); 

      // Wait until it is safe to enter (someone else might already be 
      // doing this), but also add 30 seconds max. 
      mutex.WaitOne(30000); 

      // Now let's see if some one else has added it... 
      data = (string)cache[CacheKey]; 

      // They did, so send it... 
      if (data != null) 
      { 
       return data; 
      } 


      // Still not there, so now is the time to look for it! 
      data = GetSlowFeed(context); 

      cache.Remove(CacheKey); 
      cache.Add(CacheKey, data, null, GetExpiryDate(), 
       TimeSpan.Zero, CacheItemPriority.Normal, null); 
     } 
     finally 
     { 
      // Release the Mutex. 
      if (mutex != null) 
      { 
       mutex.ReleaseMutex(); 
      } 
     } 
    } 

    return data; 
} 

Da quello che ho cercato, suggerisce che questo problema è causato da un thread di processo che tenta di rilasciare un Mutex che non ha creato, ma non capisco come ciò potrebbe accadere.

Qualcuno può suggerire come posso ri-strutturare il codice per evitare questo problema?

+0

Ho aggiunto un commento alla tua altra domanda. Tuttavia è probabilmente appropriato anche qui. Esistono provider di cache per asp.net. AppFabric è uno, Memcached è un altro. Entrambi sono molto bravi in ​​quello che fanno. Al punto che non mi preoccuperei di arrotolare il mio. – NotMe

+0

Ciao Chris, grazie per il suggerimento. Ora ho esaminato AppFabric e Memcached, ma non sembrano adatti alla mia applicazione poiché è in esecuzione in un ambiente di hosting condiviso. Come applicazioni di cache distribuita, credo che entrambi debbano essere installati a livello di server/OS che, sfortunatamente, precludono il loro uso in questa applicazione. – Neilski

risposta

13

Non si sta gestendo il caso in cui il mutex.WaitOne restituisce false ovvero timeout. Se WaitOne restituisce false, non si possiede il mutex, quindi non è necessario rilasciarlo.

bool iOwnTheMutex; 

try { 
    // set up mutex here... 
    iOwnTheMutex = mutex.WaitOne(2000); 
    if (iOwnTheMutex) { 
     // do what you need to do 
    } 
} 
finally { 
    if (mutex != null && iOwnTheMutex) { 
     mutex.ReleaseMutex(); 
    } 
}  
+0

Grazie Jack, questo ha risolto il problema. – Neilski

+0

Questa risposta non funziona più nelle app Metro. L'eccezione si verifica ancora in modo coerente, anche nel caso di test, se iOwnTheMutex è true. Sono stato in grado di ricreare con un thread (UI) prendere/rilasciare il mutex, mentre un thread separato (non-UI) prende il mutex e non è in grado di rilasciarlo, senza altri thread che tentano di prenderlo. –

+0

Il punto di forza del Mutex è la sincronizzazione tra i processi. Sembra che tu stia sincronizzando all'interno di un processo. Sono sicuro che puoi trovare qualcosa di più adatto a quello come, al più semplice, la parola chiave 'lock' in C#. –