2011-12-25 28 views
5

tengo este método:instrucción lock no parece estar funcionando

public bool Remove(EntityKeyType key) 
{ 
    lock (syncroot) 
    { 
     //wait if we need to 
     waitForContextMRE.Wait(); 

     //if the item is not local, assume it is not remote. 
     if (!localCache.ContainsKey(key)) return false; 

     //build an expression tree 
     Expression<Func<EntityType, bool>> keyComparitorExpression = GenerateKeyComparitorExpression(key); 

     var itemToDelete = TableProperty.Single(keyComparitorExpression); 

     //delete from db 
     TableProperty.DeleteOnSubmit(itemToDelete); 
     DataContext.SubmitChanges(); 

     //get the removed item for OnCollectionChanged 
     EntityType itemToRemove = localCache[key]; 
     itemToRemove.PropertyChanged -= item_PropertyChanged; 

     //remove from the list 
     Debug.Assert(localCache.Remove(key)); 

     //call the notification 
     OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, itemToRemove)); 
     return true; 
    } 
} 

Lo estoy llamando desde varios subprocesos (llamando a la misma instancia), pero una excepción que se mantenga de ser lanzado en TableProperty.Single (Secuencia contiene sin elementos). Después de depurar el código, vi que se está creando una situación en la que el elemento se elimina de la base de datos después de que un hilo diferente haya verificado la existencia del caché. Esto no debería ser posible a menos que haya múltiples subprocesos dentro de la sentencia de bloqueo (El objeto syncroot es definitivamente la misma instancia entre subprocesos).

¿Imposible? Tengo pruebas: Impossible situation

¡Hay tres hilos dentro de la instrucción de bloqueo! ¿Lo que da?

notas:

  1. La ERM se establece (no bloqueo).
  2. Esta no es una situación en la que se produce la excepción, solo muestra varios subprocesos dentro de una sección de bloqueo. Actualización: Cambié la imagen a un evento intellitrace de la excepción. La imagen anterior es here
  3. El objeto syncroot no es estático, porque solo quiero llamadas a la misma instancia sincronizadas.

actualización

Esta es la declaración del objeto SyncRoot:

private object syncroot = new object(); 

Y algunas otras declaraciones:

private ManualResetEventSlim waitForContextMRE = new ManualResetEventSlim(true); 
private DataContextType _dataContext; 
private System.Data.Linq.Table<EntityType> _tableProperty; 
//DataContextType and EntityType are generic type parameters 

No puedo hacer que el SyncRoot estática porque tengo varias instancias de la clase ejecutándose y es importante que no bloqueen k entre sí. Pero eso en realidad no importa, hacerlo estático no soluciona el problema.

El evento ManualResetEvent (waitForContextMRE) no está ahí para la sincronización: está ahí para bloquear las operaciones de la base de datos durante un cierto tiempo después de que se realizan ciertas operaciones (es decir, al inicio). Está configurado la mayor parte del tiempo. Sacarlo del bloqueo no soluciona el problema.

+1

A: ¿podemos ver dónde está inicializando 'syncroot', y B: ¿cuánto tiempo está dando vueltas el contexto de objetos? C: ¿estás seguro de que son la misma instancia? Difícil de decir desde un estado en pausa ... –

+1

Sugiero agregar mensajes de seguimiento, con System.Diagnostics.Debug.WriteLine. Escriba la identificación del hilo y el código hash syncroot al principio y al final del bloque de bloqueo. Luego puede ver fácilmente en la ventana de salida si dos hilos se superponen con el mismo syncroot, lo que definitivamente no debería suceder. –

+0

¿Cuál es el daño al hacer que syncroot sea estático? –

Respuesta

0

He estado depurando este problema por un tiempo y, aunque no lo he resuelto, es claro para mí que los bloqueos funcionan.Supongo que el problema está en DataContext (que son conocidos por ser complicados en situaciones de subprocesos múltiples).

+0

He encontrado el problema - 'Debug.Assert (localCache.Remove (key))' - Al igual que en C++, las cosas dentro de assert no se ejecutan en modo de lanzamiento, lo que hace que los elementos se eliminen de la base de datos y no del caché . –

3

La única explicación que tengo es que waitForContextMRE.Wait(); ¡Llamar esto hace que el hilo desbloquee syncroot! Y entonces otro hilo puede ingresar a la sección de bloqueo. Intenta mover waitForContextMRE.Wait(); antes de bloquear (...).

+0

No creo que podamos ver suficiente sobre waitForContextMRE para sacar conclusiones allí; 'Monitor.Wait (syncroot)' es lo único que liberaría el bloqueo, y no es obvio para mí que sea el mismo –

+0

¿Por qué llamar a wait() si está dentro del bloqueo y suponiendo que no hay otro proceso? usando el hilo? // espera si necesitamos waitForContextMRE.Wait(); –

+0

Lo intenté - no solucionó el problema desafortunadamente ... –

0

que sugieren TableProperty cerradura o DataContext

+5

Por curiosidad ... ¿basado en? Temas de enhebrado son tan complejos que ** cualquier ** reacción instintiva debe evitarse, y cualquier recomendación debe ser a: basada en la evidencia, yb: incluir el razonamiento –

+0

@Marc Gravell, gracias :-) –

+0

fue en realidad un serio punto que estaba tratando de hacer ... –

3

Creo que está llamando diferentes objetos. No hay ninguna indicación en su captura de pantalla de que esté tomando valores de diferentes hilos. Usar también syncroot no estático no es una buena idea porque puede dar lugar a casos como el tuyo. ¿Tienes una razón muy fuerte para no tenerlo estático?

+0

@Elastep Estoy de acuerdo en que la evidencia de ser el mismo objeto es bastante deficiente, de ahí mi solicitud de más información como un comentario (sin respuesta) ... –

+0

Tiene que ser no estático porque habrá múltiples instancias del objeto que no debe bloquear uno al otro. Este código se llama desde una prueba unitaria y en ese caso solo hay una instancia. –

Cuestiones relacionadas