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:
¡Hay tres hilos dentro de la instrucción de bloqueo! ¿Lo que da?
notas:
- La ERM se establece (no bloqueo).
- 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
- 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.
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 ... –
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. –
¿Cuál es el daño al hacer que syncroot sea estático? –