threads thread safe lock concurrentbag concurrent c# .net multithreading concurrency thread-safety

c# - thread - ConcurrentDictionary Pitfall: ¿Están sincronizadas las fábricas delegadas de GetOrAdd y AddOrUpdate?



thread safe queue c# (2)

La documentación de ConcurrentDictionary no establece un estado explícito, por lo que supongo que no podemos esperar que los delegados valueFactory y updateValueFactory tengan su ejecución sincronizada ( desde las operaciones GetOrAdd () y AddOrUpdate () respectivamente ).

Entonces, creo que no podemos implementar el uso de recursos dentro de ellos que necesitan control concurrente sin implementar manualmente nuestro propio control concurrente, tal vez simplemente usando [MethodImpl(MethodImplOptions.Synchronized)] sobre los delegados.

¿Estoy en lo cierto? ¿O el hecho de que ConcurrentDictionary es seguro para subprocesos podemos esperar que las llamadas a estos delegados se sincronicen automáticamente (también a prueba de subprocesos)?


No solo estos delegados no están sincronizados, sino que ni siquiera se garantiza que sucedan solo una vez. De hecho, se pueden ejecutar varias veces por llamada a AddOrUpdate .

Por ejemplo, el algoritmo para AddOrUpdate ve más o menos así.

TValue value; do { if (!TryGetValue(...)) { value = addValueFactory(key); if (!TryAddInternal(...)) { continue; } return value; } value = updateValueFactory(key); } while (!TryUpdate(...)) return value;

Tenga en cuenta dos cosas aquí.

  • No hay ningún esfuerzo para sincronizar la ejecución de los delegados.
  • Los delegados pueden ejecutarse más de una vez ya que se invocan dentro del bucle.

Así que necesitas asegurarte de hacer dos cosas.

  • Proporcione su propia sincronización para los delegados.
  • Asegúrese de que sus delegados no tengan ningún efecto secundario que dependa del número de veces que se ejecutan.

Sí, tiene razón, los delegados de usuario no están sincronizados por ConcurrentDictionary . Si los necesitas sincronizados es tu responsabilidad.

La propia MSDN dice:

Además, aunque todos los métodos de ConcurrentDictionary son seguros para subprocesos, no todos los métodos son atómicos, específicamente GetOrAdd y AddOrUpdate. El delegado de usuario que se pasa a estos métodos se invoca fuera del bloqueo interno del diccionario. (Esto se hace para evitar que el código desconocido bloquee todos los hilos).

Vea "Cómo: Agregar y quitar elementos de un ConcurrentDictionary

Esto se debe a que el ConcurrentDictionary no tiene idea de lo que hará el delegado que usted proporcione o su desempeño, por lo que si intentara bloquearlo, realmente podría impactar negativamente en el desempeño y arruinar el valor del ConcurrentDictionary.

Por lo tanto, es responsabilidad del usuario sincronizar su delegado si es necesario. El enlace de MSDN anterior en realidad tiene un buen ejemplo de las garantías que hace y no hace.