2011-01-26 10 views
6

Esto es una continuación de previous question con respecto al bloqueo en dos objetos List (Of T). La respuesta fue útil pero me dejó otra pregunta.Forma correcta de usar SyncLock (en general)

Supongamos que tengo una función como esta:

Public Function ListWork() As Integer 
    List1.Clear() 
    ..Some other work which does not modify List1.. 
    List1.AddRange(SomeArray) 
    ..Some more work that does not involve List1.. 
    Retrun List1.Count 
End Function 

que reside en una clase que declara Lista1. En un entorno multiproceso, ahora entiendo que debería tener un objeto de bloqueo privado para List1 y bloquear List1 cada vez que se modifique o enumere. Mi pregunta es, ¿debería hacer esto:

Private List1Lock As New Object 
Public Function ListWork() As Integer 
    SyncLock List1Lock 
    List1.Clear() 
    End SyncLock 
    ..Some other work which does not modify List1.. 
    SyncLock List1Lock 
    List1.AddRange(SomeArray) 
    End SyncLock 
    ..Some more work that does not involve List1.. 
    SyncLock List1Lock 
    Dim list1Count As Integer = List1.Count 
    End SyncLock 
    Retrun list1Count 
End Function 

o esto:

Private List1Lock As New Object 
Public Function ListWork() As Integer 
    SyncLock List1Lock 
    List1.Clear() 
    ..Some other work which does not modify List1.. 
    List1.AddRange(SomeArray) 
    ..Some more work that does not involve List1.. 
    Dim list1Count As Integer = List1.Count 
    End SyncLock 
    Retrun list1Count 
End Function 

supongo que el ejemplo anterior es óptima?

Respuesta

9

Según los ejemplos, es difícil decir cuál es correcto, si es que lo es. Sin embargo, algunas pautas/observaciones podrían ayudarle a responder su pregunta, o saber qué proporcionar para obtener información adicional:

Lo primero es lo primero, ¿tiene que sincronizar? ¿Tendría más sentido para cada hilo tener una instancia de esta clase? Si cada instancia es local para el hilo, y solo se modifica y utiliza en ese hilo, no es necesario bloquearlo.

Si el propósito de esta clase y la utilización de subprocesos es para el procesamiento paralelo de un conjunto de datos más grande, puede tener más sentido para el subproceso principal dividir la tarea de alguna manera lógica y luego esperar a que los subprocesos de trabajo completar. En este caso, en lugar de administrar los hilos por su cuenta, busque en el ThreadPool y aguarde los identificadores. La mayor parte del trabajo sucio ya está hecho para ti.

Acerca de la sincronización/bloqueo en general: Si su operación se interrumpió entre los pasos, ¿los datos serían consistentes/válidos?

En su ejemplo, supongamos que tiene dos hilos. El primero está en el área entre .AddRange() y .Count, cuando entra el segundo hilo entra la función y adquiere el bloqueo en la lista.

El subproceso 1 se ejecuta un poco más, y toca el candado que protege el método .Count, y se va a dormir. Mientras tanto, el hilo 2 borra la lista y luego suelta su bloqueo, levantando el hilo 1, que luego adquiere el bloqueo.

En este caso, el subproceso 1 tendrá 0 devuelto por esta función, cuando el trabajo fue realizado por el subproceso 1 para compilar la lista.Y luego, la longitud de la lista no será realmente 0, ya que el hilo 2 apareció y llenó la lista.

En este caso, los bloqueos alrededor de las operaciones de lista individuales rompen el programa, por lo que tiene más sentido tener un bloqueo entre Clear y la llamada Count.

En resumen, multi-threading es una buena manera de introducir una clase completa de errores sutiles relacionados con Race Conditions, que a menudo resultan en Heisenbugs.

A menudo es aconsejable evitar los hilos cuando sea posible. Si no puede, intente organizar su carga de trabajo de forma que requiera una sincronización mínima (por ejemplo, al inicio del hilo un conjunto de datos y luego esperar que indique la finalización, como con el ejemplo del grupo de subprocesos vinculado). Si no puede hacer eso, pise con cuidado, y siempre pregúntese "¿Qué pasará si dos hilos se ejecutan en esta área".

Esperemos que esto lo ayude a prepararse para futuras aventuras con código de subprocesos múltiples.

+0

Creo que entiendo esto ahora. Bastante sutil. Debo tomar una clase o algo ... Gracias por la información. –

6

"Depende". Los dos ejemplos tienen una semántica diferente.

En el último ejemplo, el conjunto completo de operaciones es atómico con respecto a la cerradura. Mientras que en el ejemplo anterior, el acceso a la lista está protegido en bloque, pero el conjunto completo de operaciones no se puede (correctamente) ver como atómico (con respecto al bloqueo).

Imagine lo que podría/podría ocurrir en términos si operación/entrelazado de subprocesos si ListWork se invoca en el mismo objeto por diferentes subprocesos.

Happy coding.

Cuestiones relacionadas