Pregunta

Estoy refactorizando algún código y me pregunto sobre el uso de un lock en el constructor de instancias.

public class MyClass {

    private static Int32 counter = 0;
    private Int32 myCount;

    public MyClass() {

        lock(this) {
            counter++;
            myCount = counter;
        }
    }
}

Por favor confirmar

  1. Los constructores de instancias son seguros para subprocesos.
  2. La declaración de bloqueo impide el acceso a ese bloque de código, no al miembro "contador" estático.

Si la intención del programador original fuera que cada instancia conociera su "recuento", ¿cómo sincronizaría el acceso al miembro "contador" para garantizar que otro hilo no sea nuevo? MyClass ¿Y cambiar el conteo antes de que este establezca su conteo?

Para su información: esta clase no es un singleton.Las instancias simplemente deben ser conscientes de su número.

¿Fue útil?

Solución

@ajmastrean

No estoy diciendo que debas usar el patrón singleton en sí, sino adoptar su método para encapsular el proceso de creación de instancias.

es decir.

  • Haz que el constructor sea privado.
  • Cree un método de instancia estática que devuelva el tipo.
  • En el método de instancia estática, use la palabra clave lock antes de crear una instancia.
  • Cree una instancia nueva del tipo.
  • Incrementar el conteo.
  • Desbloquee y devuelva la nueva instancia.

EDITAR

Un problema que se me ha ocurrido, ¿cómo sabrías cuándo ha bajado el conteo?;)

EDITAR DE NUEVO

Pensando en ello, podrías agregar código al destructor que llame a otro método estático para disminuir el contador :D

Otros consejos

Si sólo estás incrementando un número, hay una clase especial (Interlocked) solo para eso...

http://msdn.microsoft.com/en-us/library/system.threading.interlocked.increment.aspx

Método de incremento entrelazado

Incrementa una variable especificada y almacena el resultado, como una operación atómica.

System.Threading.Interlocked.Increment(myField);

Más información sobre las mejores prácticas de subprocesamiento...

http://msdn.microsoft.com/en-us/library/1c9txz50.aspx

Supongo que esto es para un patrón singleton o algo parecido.Lo que quieres hacer no es bloquear tu objeto, sino bloquear el contador mientras lo modificas.

private static int counter = 0;
private static object counterLock = new Object();

lock(counterLock) {
    counter++;
    myCounter = counter;
}

Porque su código actual es algo redundante.Especialmente en el constructor donde solo hay un subproceso que puede llamar a un constructor, a diferencia de los métodos donde se puede compartir entre subprocesos y se puede acceder a él desde cualquier subproceso que se comparta.

Por lo poco que puedo decir de tu código, estás intentando darle al objeto el recuento actual en el momento de su creación.Entonces, con el código anterior, el contador se bloqueará mientras se actualiza y configura localmente.Por lo tanto, todos los demás constructores tendrán que esperar a que se publique el contador.

Puedes usar otro objeto estático para bloquearlo.

private static Object lockObj = new Object();

y bloquear este objeto en el constructor.

lock(lockObj){}

Sin embargo, no estoy seguro de si hay situaciones que deban manejarse debido a la optimización del compilador en .NET como en el caso de java

La forma más eficaz de hacerlo sería utilizar la operación de incremento entrelazado.Incrementará el contador y devolverá el valor recién establecido del contador estático de una vez (atómicamente)

class MyClass {

    static int _LastInstanceId = 0;
    private readonly int instanceId; 

    public MyClass() { 
        this.instanceId = Interlocked.Increment(ref _LastInstanceId);  
    }
}

En su ejemplo original, la instrucción lock(this) no tendrá el efecto deseado porque cada instancia individual tendrá una referencia "this" diferente y, por lo tanto, varias instancias podrían actualizar el miembro estático al mismo tiempo.

En cierto sentido, se puede considerar que los constructores son seguros para subprocesos porque la referencia al objeto que se está construyendo no es visible hasta que el constructor se ha completado, pero esto no sirve de nada para proteger una variable estática.

(Mike Schall tenía la broca entrelazada primero)

Creo que si modificas el Patrón singleton para incluir un recuento (obviamente usando el método seguro para subprocesos), estará bien :)

Editar

¡Mierda que borré accidentalmente!

No estoy seguro si los constructores de instancias SON Seguro para subprocesos, recuerdo haber leído sobre esto en un libro de patrones de diseño, es necesario asegurarse de que los bloqueos estén en su lugar durante el proceso de creación de instancias, simplemente por esto.

@Robar

Para su información, es posible que esta clase no sea única, necesito acceso a diferentes instancias.Simplemente deben mantener un recuento.¿Qué parte del patrón singleton cambiaría para realizar un incremento de 'contador'?

¿O está sugiriendo que exponga un método estático para la construcción que bloquea el acceso al código que incrementa y lee el contador con un candado?

public MyClass {

    private static Int32 counter = 0;
    public static MyClass GetAnInstance() {

        lock(MyClass) {
            counter++;
            return new MyClass();
        }
    }

    private Int32 myCount;
    private MyClass() {
        myCount = counter;
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top