¿Por qué el objeto de bloqueo tiene que ser estático?

112

Es muy común utilizar un objeto de solo lectura estático privado para bloquear en subprocesos múltiples. Entiendo que privado reduce los puntos de entrada al objeto de bloqueo al apretar la encapsulación y por lo tanto el acceso a lo más esencial.

¿Pero por qué estática?

private static readonly object Locker = new object();

Al final, el campo solo se usa dentro de mi clase, y también podría usar esto en su lugar:

private readonly object Locker = new object();

¿Algún comentario?

ACTUALIZAR:

Como ejemplo, he pegado este código (solo un ejemplo). Podría usar un casillero estático o no estático en esto y ambos funcionarían bien. Teniendo en cuenta la respuesta a continuación, ¿debería definir mi casillero de esta manera? (Lo siento, tengo una entrevista la próxima semana y necesito conocer todos los detalles :)

private readonly object Locker = new object();

Y aquí está el código:

    private int _priceA;
    private int _priceB;
    private EventWaitHandle[] _waithandle;
    private readonly IService _service;

//ctor
public ModuleAViewModel(IService service)
    {
        _service = service;
        _modelA = new ModelA();
        _waithandle = new ManualResetEvent[2];
        _waithandle[0] = new ManualResetEvent(false);
        _waithandle[1] = new ManualResetEvent(false);
        LoadDataByThread();
    }


 private void LoadDataByThread()
        {
            new Thread(() =>
                           {
                               new Thread(() =>
                               {
                                   lock (Locker)
                                   {
                                       _priceA = _service.GetPriceA();
                                   }
                                   _waithandle[0].Set();
                               }).Start();

                               new Thread(() =>
                               {
                                   lock (Locker)
                                   {
                                       _priceB = _service.GetPriceB();
                                   }
                                   _waithandle[1].Set();
                               }).Start();

                               WaitHandle.WaitAll(_waithandle);
                               PriceA = _priceA;
                               PriceB = _priceB;
                           }).Start();
        }

Gracias

Houman
fuente
15
Que yo sepa, la estática se usa generalmente para que sea independiente de la instancia. Si existen varias instancias de "MyWorkerClass", solo una se puede ejecutar con los datos proporcionados a la vez (asumiendo que todas usan recursos compartidos).
Brad Christie
2
La edición carece de un detalle importante: ¿dónde están _servicey _waithandleubicados? ¿ejemplo? ¿estático? ¿otro? Eso podría , por ejemplo, sincronizar deliberadamente el acceso a un servidor remoto ...
Marc Gravell
correcto, con la segunda edición: sí, desde este extremo de las cosas podría bloquear por instancia. Sin embargo, puede haber razones para hacerlo estático: si el desarrollador original quería (como se mencionó) sincronizar el acceso para que el servidor solo reciba una solicitud a la vez de este AppDomain ... No puedo saber si ese es el caso , o si fue simplemente accidental.
Marc Gravell

Respuestas:

177

No es "muy común usar un objeto de solo lectura estático privado para bloquear en subprocesos múltiples"; más bien, es común usar un bloqueo en la granularidad apropiada / elegida . A veces eso es static. Más a menudo, en mi opinión, no lo es, pero se basa en instancias .

La principal vez que ve un staticbloqueo es para un caché global o para la carga diferida de datos globales / singletons. Y en este último, hay mejores formas de hacerlo de todos modos .

Entonces realmente depende: ¿cómo se Lockerusa en su escenario? ¿Está protegiendo algo que en sí mismo es estático? Si es así, la cerradura debe ser estática. Si está protegiendo algo que está basado en instancias , entonces, en mi opinión, el bloqueo también debe basarse en instancias.

Marc Gravell
fuente
24
¿Podría dar más detalles sobre una mejor manera de diferir la carga de datos globales?
bizi
Siempre uso estático / volátil porque si hay varios lugares en los que se basa en instancias, todavía querría controlar el acceso a mi método / variable de una manera segura para subprocesos. Muchas instancias pueden estar accediendo a los mismos recursos y quiero controlar eso. Yo también me gustaría ver lo mejor de hacer esto. Tiene una gran reputación y estoy seguro de que su respuesta será igualmente excelente para mí. ¿Por favor responde?
Andrew Simpson
82

No tiene por qué ser estática, de hecho, a veces se debe no ser estática.

La variable debe vivir en el mismo ámbito que los métodos donde la usa para bloquear. Si los métodos son estáticos, la variable debe ser estática, y si los métodos son métodos de instancia, la variable debe ser una variable de instancia.

Una variable estática seguirá funcionando cuando se use para bloquear un método de instancia, pero entonces bloqueará demasiado. Bloqueará todos los métodos en todas las instancias, no solo los métodos en la misma instancia.

Guffa
fuente
28
+1 para el "a-ha" ... Bloqueará todos los métodos en todas las instancias, no solo los métodos en la misma instancia.
radarbob
3
@radarbob - Detalle menor: no bloquearás todos los métodos , solo tomas un bloqueo que podría interesar a más clientes. Los métodos nunca se bloquean, es solo que se ha tomado el mutex.
Erno
Sospecho que la redacción de esta respuesta podría ser engañosa: el bloqueo no debería tener nada que ver con el alcance de los métodos; solo debería preocuparse por el alcance de los datos compartidos a los que se accede en esos métodos. Es posible que el método de instancia no acceda a ningún dato compartido (y, por lo tanto, no sea necesario bloquearlo), puede acceder a datos compartidos estáticos (y, por lo tanto, necesitar un bloqueo estático, también puede ser una buena idea la refactorización), lo mismo para estático ...
Alexei Levenkov
@AlexeiLevenkov: Tiene razón en que el alcance debe decidirse realmente por si los datos son estáticos o no, pero el alcance de los métodos también debe decidirse por eso, por lo que todo encaja. Los datos de la instancia normalmente no necesitan bloquearse, pero si la instancia se comparte entre subprocesos, entonces necesitaría bloquearse.
Guffa
28

El alcance y la vida útil de un candado pueden / deben depender de la "cosa" que desee bloquear. Los bloqueos estáticos se utilizan principalmente para bloquear elementos estáticos.

Erno
fuente
3
Detalle menor: el candado no es estático, el objeto que usa para identificar el candado es estático. Otro detalle menor: no bloqueas "cosas".
Guffa
2
Sí, creo que si intentamos bloquear las "cosas" equivocadas, es posible que sean demasiado grandes y fuertes, y algún día se escapen.
ProfK
12
@Guffa Eso es extraño, en un comentario de arriba dijiste correctamente: "Simplemente estás complicando demasiado las cosas", ahora veo 1 minuto antes de decir eso, parece que estabas complicando demasiado las cosas :)
Nicholas Petersen