2010-03-28 16 views
8

He estado haciendo multi-threading simple en VB.NET por un tiempo, y acabo de entrar en mi primer gran proyecto de múltiples subprocesos. Siempre he hecho todo usando la declaración Synclock porque no creía que hubiera una mejor manera.¿Por qué usar SyncLocks en .NET para operaciones simples cuando la clase Interlocked está disponible?

acabo de aprender acerca de la clase Interlocked - que hace que parezca como si todo esto:

Private SomeInt as Integer 
Private SomeInt_LockObject as New Object 

Public Sub IntrementSomeInt 
    Synclock SomeInt_LockObject 
     SomeInt += 1 
    End Synclock 
End Sub 

se puede reemplazar con una sola instrucción:

Interlocked.Increment(SomeInt) 

Este se encarga de todo el bloqueo interno y modifica el número. Esto sería mucho más simple que escribir mis propios bloqueos para operaciones simples (las operaciones más largas o más complicadas obviamente aún necesitan su propio bloqueo).

¿Hay algún motivo por el que haya activado mi propio bloqueo, utilizando objetos de bloqueo dedicados, cuando puedo lograr lo mismo con los métodos Interlocked?

Respuesta

8

Estás en lo correcto; Interlocked se debe utilizar aquí, y será más rápido que SyncLock.
Sin embargo, la clase Interlocked no es muy conocida.

Sin embargo, hay situaciones en las que necesita utilizar SyncLock y Interlocked no ayudará.

+0

¿Puede darnos un ejemplo de tal situación? Si solo estoy haciendo un incremento/decremento simple (o .Add si necesito cambiar el valor en más de 1), parece que Interlocked sería mejor en todos los casos. Si necesito algo más avanzado (como un proceso de 4 pasos donde necesito garantizar que es el único hilo que lo ejecuta en un momento dado), necesito un poco de sincronización. Me interesa una situación en la que Interlocked parezca apropiado, pero es una mala elección. – SqlRyan

+3

Si está trabajando con dos variables diferentes, 'Interbloqueado' no es suficiente. – SLaks

4

Esto es porque nadie lo sabe. ¡Difundir la palabra!

+1

+1 Buena respuesta. – SLaks

+0

Eso es lo que pensé - Veo que ha estado presente desde 1.0, y me quedé asombrado cuando lo encontré. Pensé "Tiene que haber algo más aquí que no estoy viendo, o este método de bloqueo sería mucho más popular".Realmente solo quiero asegurarme de que sea tan bueno como parece a primera vista. – SqlRyan

+1

Esta respuesta no transmite suficiente información, ni responde directamente la pregunta. – Kir

3

La respuesta corta es porque el uso de una cerradura Monitor (SyncLock en VB y lock { } en C#) no sólo asegura que sólo un hilo a la vez puede acceder a la variable (o, en un sentido estricto, sólo un hilo a la vez puede obtener un bloqueo en el objeto de bloqueo), pero también crea la barrera de memoria necesaria para garantizar que las lecturas de la variable no se optimicen.

Si nunca está simplemente leyendo el valor de la variable (en otras palabras, todo su trabajo se realiza a través de llamadas al Interlocked), entonces estará bien. Sin embargo, si necesita poder realizar una lectura normal de la variable, entonces la situación es más complicada. Las lecturas/escrituras sin bloqueo generalmente se realizan en C# usando la palabra clave volatile. Esto instruye al compilador a leer el valor de la variable en cualquier lugar que se use, en lugar de optimizar cualquiera de estas lecturas en un caché local. Lamentablemente, no hay un equivalente en VB.NET, por lo que tendrá que usar algo más.

La respuesta aceptada a this question debe proporcionar algo más de información sobre lo que puede hacer. En resumen, la mayoría de las personas usa SyncLock en VB.NET porque es más fácil y menos complicado que la lógica requerida para hacerlo sinSyncLock.

+0

No del todo correcto, cuando cambia sus variables con enclavamiento, puede leerlas de forma segura sin ninguna medida especial. Debido a que la barrera está alrededor de las escrituras, las lecturas son seguras. –

+0

@Henk: ¿No podrían optimizarse múltiples lecturas posteriores a la llamada 'Interbloqueada 'en una sola lectura en caché? –

+0

Adam, sí podrían. Y desde una perspectiva en tiempo real que podría parecer 'incorrecta' pero no conduciría a un comportamiento incorrecto. Verifique los miembros enclavados. Solo hay una lectura para valores de 64 bits (en sistemas de 32 bits). http://msdn.microsoft.com/en-us/library/system.threading.interlocked_members.aspx –

1

Interlocked se limita a las operaciones simples en Integer, Long y Boolean y tal.

Si desea agregar un elemento a una Lista compartida (de T), por ejemplo, todavía necesitará SynClock.

+0

De hecho, eso se debe a que se trata de una operación atómica implementada a nivel de hardware, es decir, la CPU garantizará que el incremento no se pueda contestar, por lo que debe correlacionarse con primitivas de la CPU, como ints, longs, etc. – zebrabox

2

Una vez leí una explicación muy buena sobre las llamadas operaciones no atómicas y atómicas (en VB: entrelazadas) e intentaré resumirlas.

Normal "non-atomic" operations consist of several steps 

-> otros hilos pueden trabajar entre esos estreptococos

"Atomic" operations consist of one only one step 

-> otros temas no pueden realizar el trabajo mientras que las operaciones atómicas son procesadas, las operaciones atómicas se procesan siempre como a toda

El Interlocked la clase es una colección de tales operaciones atómicas y, por lo tanto, ensartable por definición. Incluso con varios subprocesos que realizan operaciones de lectura y escritura en la misma variable, estas operaciones son absolutamente seguras para subprocesos.

Aún así, una combinación de estos comandos con seguridad de hilo puede ser insegura, ya que las condiciones de carrera pueden ocurrir entre las operaciones atómicas.

Así que si quiere, por ejemplo, comparar 2 variables y luego incrementar la más pequeña, esto no es seguro para subprocesos aunque las operaciones individuales para sí mismas son (interlocked.compare, interlocked.increment). Aquí todavía tiene que usar sincronizaciones.

Aparte de esa limitación, no hay un "lado oculto" de enclavamiento.

Un ejemplo de una condición de carrera con a = 5:

Thread1: a+=1 
Thread2: a+=2  
--> supposed to be 8, but can be only 6 or 7, 
but can also be 6 or 7 depending on which thread wins the race 

opción 1:

T1 step 1: read 5 
T1 step 2: add 1 = 6 
T1 step 3: write 6 
T2 step 1: read 6 
T2 step 2: add 2 = 8 
T2 step 3: write 8 
--> is 8 as supposed 

o la opción 2:

T1 step 1: read 5 
T2 step 1: read 5 
T1 step 2: add 1 = 6 
T2 step 2: add 2 = 7 
T2 step 3: write 7 
T1 step 3: write 6 
--> is only 6 

o la opción 3:

Con interlocked.increment:

la opción 1:

T1 step 1: read 5, add 1, write 6 
T2 step 1: read 6, add 2, write 8 

o la opción 2:

T2 step 1: read 5, add 2, write 7 
T1 step 1: read 7, add 1, write 8 

-> En todos los casos a = 8 como se supone, para las hebras solución

Todos las preguntas que se publicaron aquí se pueden resolver aplicando este ejemplo simple al código cuestionable.

Espero que esto ayude a otras personas que buscan en este tema. Janis

Cuestiones relacionadas