Lo peor (en realidad no funcionará)
Cambiar el modificador de acceso de counter
apublic volatile
Como han mencionado otras personas, esto por sí solo no es realmente seguro en absoluto. El punto volatile
es que varios subprocesos que se ejecutan en varias CPU pueden almacenar datos en caché y volverán a ordenar las instrucciones.
Si no es así volatile
, y la CPU A incrementa un valor, entonces la CPU B puede no ver ese valor incrementado hasta algún tiempo después, lo que puede causar problemas.
Si es así volatile
, esto solo garantiza que las dos CPU vean los mismos datos al mismo tiempo. No les impide en absoluto intercalar sus operaciones de lectura y escritura, que es el problema que está tratando de evitar.
Segundo mejor:
lock(this.locker) this.counter++
;
Esto es seguro (siempre y cuando recuerdes en lock
cualquier otro lugar al que accedas this.counter
). Impide que cualquier otro subproceso ejecute cualquier otro código que esté protegido por locker
. El uso de bloqueos también evita los problemas de reordenamiento de múltiples CPU como se indicó anteriormente, lo cual es excelente.
El problema es que el bloqueo es lento, y si vuelve a usarlo locker
en otro lugar que no está realmente relacionado, puede terminar bloqueando sus otros hilos sin ningún motivo.
Mejor
Interlocked.Increment(ref this.counter);
Esto es seguro, ya que efectivamente hace la lectura, el incremento y la escritura en 'un golpe' que no se puede interrumpir. Debido a esto, no afectará a ningún otro código, y tampoco es necesario que recuerde bloquear en otro lugar. También es muy rápido (como dice MSDN, en las CPU modernas, esto es literalmente una sola instrucción de CPU).
Sin embargo, no estoy del todo seguro si se soluciona con otras CPU que reordenan cosas, o si también necesita combinar volátiles con el incremento.
Notas entrelazadas:
- LOS MÉTODOS ENCLAVADOS SON CONCURRENTEMENTE SEGUROS EN CUALQUIER NÚMERO DE NÚCLEOS O CPU.
- Los métodos enclavados aplican una cerca completa alrededor de las instrucciones que ejecutan, por lo que no se reordena.
- Los métodos enclavados no necesitan o ni siquiera admiten el acceso a un campo volátil , ya que el volátil se coloca media cerca alrededor de las operaciones en un campo determinado y enclavado está usando la cerca completa.
Nota al pie: Para qué es volátil en realidad es bueno.
Como volatile
no evita este tipo de problemas de subprocesos múltiples, ¿para qué sirve? Un buen ejemplo es decir que tiene dos hilos, uno que siempre escribe en una variable (digamos queueLength
) y otro que siempre lee de esa misma variable.
Si queueLength
no es volátil, el hilo A puede escribir cinco veces, pero el hilo B puede ver que esas escrituras están retrasadas (o incluso potencialmente en el orden incorrecto).
Una solución sería bloquear, pero también podría usar volátiles en esta situación. Esto aseguraría que el hilo B siempre verá lo más actualizado que el hilo A ha escrito. Sin embargo , tenga en cuenta que esta lógica solo funciona si tiene escritores que nunca leen, y lectores que nunca escriben, y si lo que está escribiendo es un valor atómico. Tan pronto como realice una sola lectura-modificación-escritura, deberá ir a Operaciones enclavadas o usar un Bloqueo.