2012-09-14 25 views
8

He leído algo sobre SyncRoot pattern como regla general para evitar interbloqueos. Y al leer una pregunta de hace algunos años (vea esto link), creo que entiendo que algunos usos de este patrón pueden ser incorrectos. En particular, me he centrado en las siguientes frases de this topic:Algunas aclaraciones sobre el patrón SyncRoot: ¿cuál es la forma correcta de usar este patrón?

Se dará cuenta de una propiedad SyncRoot en muchas de las Colecciones en System.Collections. En retrospectiva, creo que esta propiedad fue un error ... Tenga la seguridad de que no cometeremos el mismo error ya que construimos las versiones genéricas de estas colecciones.

De hecho, por ejemplo, List<T> clase no implementa SyncRoot propiedad, o más correctamente que se implementa de manera explícita (véase this answer), por lo que debe convertir a ICollection con el fin de usarlo. Pero this comment argumenta que hacer público un campo privado SyncRoot es una mala práctica como el bloqueo en this (ver this answer), como también se confirmó en this comment.

Por lo tanto, si entiendo correctamente, cuando implemente una estructura de datos no segura para subprocesos, ya que podría usarse en un contexto multiproceso, no debería (en realidad, no debo) proporcionar la propiedad SyncRoot. Pero debería dejar al desarrollador (que usará esta estructura de datos) la tarea de asociarlo con un objeto SyncRoot privado como en el siguiente código de ejemplo.

public class A 
{ 
    private MyNonThreadSafeDataStructure list; 
    private readonly object list_SyncRoot = new object; 

    public Method1() 
    { 
     lock(list_SyncRoot) 
     { 
      // access to "list" private field 
     } 
    } 

    public Method2() 
    { 
     lock(list_SyncRoot) 
     { 
      // access to "list" private field 
     } 
    } 
} 

En resumen, entiende que las mejores prácticas para la sincronización/bloqueo debe ser el siguiente:

  1. Cualquier objeto SyncRoot privadas no deben ser expuestos a través de una propiedad pública; en otras palabras, una estructura de datos personalizada no debe proporcionar una propiedad pública SyncRoot (consulte también this comment).
  2. En general, no es obligatorio utilizar objetos privados para el bloqueo (consulte this answer).
  3. Si una clase tiene varios conjuntos de operaciones que deben sincronizarse, pero no entre sí, debe tener múltiples objetos SyncRoot privados (consulte this comment).

¿Cuál es el significado del uso adecuado de este patrón?

+0

Mi opinión es que si quieres hacer una clase segura para subprocesos internamente, haces el bloqueo internamente (en privado) y documentas que la clase es segura para subprocesos en general. De lo contrario, haga _no lock_, documente que la clase no es segura para subprocesos, y permita que los consumidores de su clase implementen su propio bloqueo según lo necesiten para su uso específico. –

Respuesta

4

que evitaría la adición de una propiedad SyncRoot al tipo que diseño, aquí están las razones:

  • Los usuarios de mi tipo pueden necesitar utilizar diferentes mecanismos de sincronización, por ejemplo Mutex o ReaderWriterLock o ReaderWriterLockSlim etc

  • El tipo se vuelve más gordo: su responsabilidad se vuelve más dispersa. ¿Por qué debería agregar soporte para el bloqueo explícito de subprocesos múltiples y no hay soporte para otras pelusas? Me obligar al usuario a seguir solamente una práctica, que puede no ser la mejor solución en todos los casos

  • que tendría que aplicar la propiedad correctamente (sin volver this o typeof(MyClass)), es decir, esto es incorrecto:

    public object SyncRoot {get {return this;}} 
    

también me gustaría evitar el uso de SyncRoot propiedad de los tipos de .NET Framework. Si necesito hacer un tipo sin protección SyncRoot con propiedad threadsafe, usaré un patrón de bloqueo, y si un tipo tiene esta propiedad, aún no optaré por el bloqueo en SyncRoot. Esto hace que mi estilo de código sea consistente y más fácil de leer/mantener.

0

Hay una serie de conceptos aquí. En primer lugar, lo que ha implementado correctamente es una clase segura para subprocesos en la que ningún consumidor de la clase tendría que hacer su propia sincronización. Por lo tanto, no hay absolutamente ninguna necesidad de exponer el objeto syncRoot. En las antiguas clases de Colección, la propiedad SyncRoot estaba expuesta porque las clases eran no thread-safe.

Al bloquear un objeto arbitrario y bloquear su colección interna, no hay absolutamente ninguna diferencia en términos de corrección o rendimiento del programa. Siempre y cuando la referencia a ambos no cambie, funcionan tan bien como los parámetros del monitor. Entrar/Salir. ¿Cambiará tu colección interior? Si no, márquelo también como si fuera de lectura.

En tercer lugar está el comentario sobre el uso de diferentes bloqueos basados ​​en diferentes operaciones. El ejemplo clásico de esto es ReaderWriterLock.Debería analizar la necesidad de utilizar diferentes niveles de bloqueos en función de las diferentes funcionalidades expuestas por su clase.

Cuestiones relacionadas