uso unity template lazy explained example ejemplo code c# .net multithreading singleton thread-safety

c# - unity - Uso seguro de subprocesos de los miembros de un singleton



singleton template c# (9)

¿Puedo suponer que el patrón singleton expone mi clase de seguridad de subprocesos adorable a todos los problemas de subprocesos de los miembros estáticos regulares?

No. Tu clase simplemente no es insegura. El singleton no tiene nada que ver con eso.

(Estoy entendiendo el hecho de que los miembros de instancias llamaron a un objeto estático para provocar problemas)

Tampoco tiene nada que ver con eso.

Tienes que pensar así: ¿es posible en mi programa que 2 (o más) hilos accedan a este dato al mismo tiempo?

No importa el hecho de que obtenga los datos a través de una variable única o estática, o que pase un objeto como parámetro de método. Al final del día, todo son solo algunos bits y bytes en la RAM de su PC, y todo lo que importa es si múltiples hilos pueden ver los mismos bits.

Tengo una clase simple de C # que usan múltiples clases. ¿El acceso a través de Instance al método Toggle() es seguro para subprocesos? En caso afirmativo, ¿qué suposiciones, reglas, etc. Si no, por qué y cómo puedo solucionarlo?

public class MyClass { private static readonly MyClass instance = new MyClass(); public static MyClass Instance { get { return instance; } } private int value = 0; public int Toggle() { if(value == 0) { value = 1; } else if(value == 1) { value = 0; } return value; } }


Eso es lo que pense. Pero, estoy buscando los detalles ... ''Toggle ()'' no es un método estático, pero es un miembro de una propiedad estática (cuando se usa ''Instancia''). ¿Eso es lo que lo hace compartido entre los hilos?

Si su aplicación es de subprocesos múltiples y puede prever que múltiples subprocesos tendrán acceso a ese método, eso la hace compartida entre subprocesos. Debido a que su clase es Singleton, usted sabe que el hilo diferente tendrá acceso al MISMO objeto, así que tenga cuidado con la seguridad de los hilos de sus métodos.

¿Y cómo se aplica esto a los singletons en general? ¿Tendría que abordar esto en todos los métodos de mi clase?

Como dije antes, como es un singleton, usted sabe que un hilo diferente accederá al mismo objeto, posiblemente al mismo tiempo. Esto no significa que tenga que hacer que cada método obtenga un bloqueo. Si observa que una invocación simultánea puede conducir a un estado dañado de la clase, entonces debe aplicar el método mencionado por @Thomas


Bueno, en realidad no conozco C # tan bien ... pero estoy bien en Java, así que daré la respuesta para eso, y espero que los dos sean lo suficientemente similares como para que sean útiles. Si no, me disculpo.

La respuesta es, no, no es seguro. Un hilo podría llamar a Toggle () al mismo tiempo que el otro, y es posible, aunque poco probable con este código, que Thread1 pueda establecer un value entre las veces que Thread2 lo comprueba y cuándo lo establece.

Para solucionarlo, simplemente haga que Toggle () esté synchronized . No bloquea nada ni llama a nada que pueda engendrar otro hilo que podría llamar a Toggle (), así que eso es todo lo que tienes que hacer para salvarlo.


La implementación original no es segura para subprocesos, como señala Ben

Una forma sencilla de hacerlo seguro es introducir una instrucción de bloqueo. P.ej. Me gusta esto:

public class MyClass { private Object thisLock = new Object(); private static readonly MyClass instance = new MyClass(); public static MyClass Instance { get { return instance; } } private Int32 value = 0; public Int32 Toggle() { lock(thisLock) { if(value == 0) { value = 1; } else if(value == 1) { value = 0; } return value; } } }


También agregaría un constructor protegido a MyClass para evitar que el compilador genere un constructor predeterminado público.


Su hilo podría detenerse en medio de ese método y transferir el control a un hilo diferente. Necesitas una sección crítica sobre ese código ...

private static object _lockDummy = new object(); ... lock(_lockDummy) { //do stuff }


Estaba pensando que si volcara el patrón singleton y forzaba a todos a obtener una nueva instancia de la clase, aliviaría algunos problemas ... pero eso no impide que nadie inicialice un objeto estático de ese tipo y lo pase por alto. .. o desde el spinning de múltiples hilos, todos accediendo a ''Toggle ()'' desde la misma instancia.

Bingo :-)

Ahora lo entiendo. Es un mundo difícil. Desearía no estar refaccionando el código heredado :(

Desafortunadamente, el multihilo es difícil y tienes que ser muy paranoico sobre las cosas :-) La solución más simple en este caso es seguir con el singleton, y agregar un bloqueo alrededor del valor, como en los ejemplos.


Citar:

if(value == 0) { value = 1; } if(value == 1) { value = 0; } return value;

value siempre será 0 ...


¿El acceso a través de ''Instancia'' a la clase ''Toggle ()'' es seguro? En caso afirmativo, ¿qué suposiciones, reglas, etc. Si no, por qué y cómo puedo solucionarlo?

No, no es threadsafe

Básicamente, ambos hilos pueden ejecutar la función Toggle al mismo tiempo, por lo que esto podría suceder

// thread 1 is running this code if(value == 0) { value = 1; // RIGHT NOW, thread 2 steps in. // It sees value as 1, so runs the other branch, and changes it to 0 // This causes your method to return 0 even though you actually want 1 } else if(value == 1) { value = 0; } return value;

Debe operar con la siguiente suposición.

Si se están ejecutando 2 subprocesos, pueden y se intercalarán e interactuarán entre sí al azar en cualquier punto. Puede estar escribiendo o leyendo un número entero de 64 bits o flotante (en una CPU de 32 bits) y otro hilo puede saltar y cambiarlo desde debajo de usted.

Si los 2 hilos nunca tienen acceso a nada en común, no importa, pero tan pronto como lo hagan, debes evitar que se pisen los dedos del pie. La forma de hacer esto en .NET es con bloqueos.

Puede decidir qué y dónde bloquear pensando en cosas como esta:

Para un bloque de código dado, si el valor de something se cambió de debajo de mí, ¿importaría? Si lo hiciera, debes bloquear ese something por la duración del código donde sea importante.

Mirando tu ejemplo otra vez

// we read value here if(value == 0) { value = 1; } else if(value == 1) { value = 0; } // and we return it here return value;

Para que esto devuelva lo que esperamos, suponemos que el value no cambiará entre la lectura y la return . Para que esta suposición sea realmente correcta, debe bloquear el value de la duración de ese bloque de código.

Entonces harías esto:

lock( value ) { if(value == 0) ... // all your code here return value; }

SIN EMBARGO

En .NET solo puede bloquear Tipos de referencia. Int32 es un tipo de valor, por lo que no podemos bloquearlo.
Resolvemos esto introduciendo un objeto "ficticio" y bloqueándolo donde deseamos bloquear el "valor".

Esto es a lo que Ben Scheirman se refiere.