Por que o objeto de bloqueio precisa ser estático?

112

É muito comum usar um objeto somente leitura estático privado para bloqueio em multiencadeamento. Eu entendo que privado reduz os pontos de entrada para o objeto de bloqueio, apertando o encapsulamento e, portanto, o acesso ao mais essencial.

Mas por que estático?

private static readonly object Locker = new object();

No final, o campo é usado apenas dentro da minha classe, e eu também poderia usar apenas este:

private readonly object Locker = new object();

Algum comentário?

ATUALIZAR:

Como exemplo, colei este código (apenas um exemplo). Eu poderia usar um armário estático ou não estático nisso e ambos funcionariam bem. Considerando a resposta abaixo, eu deveria definir meu armário dessa maneira? (Desculpe, tenho uma entrevista na próxima semana e preciso saber todos os detalhes :)

private readonly object Locker = new object();

E aqui está o 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();
        }

obrigado

Houman
fonte
15
Até onde sei, estático é geralmente usado para torná-lo independente de instância. Se existirem várias instâncias de "MyWorkerClass", apenas uma pode ser executada com os dados fornecidos por vez (assumindo que todas usam recursos compartilhados).
Brad Christie
2
A edição carece de um detalhe importante: onde estão _servicee _waithandlelocalizados? instância? estático? de outros? Isso poderia ser , por exemplo, sincronizar deliberadamente o acesso a um servidor remoto ...
Marc Gravell
certo, com a segunda edição: sim, a partir desse final você poderia bloquear por instância. Não pode ter sido razões para torná-lo estático, embora - se o dev originais queria (como mencionado) para acesso de sincronização para que o servidor só recebe um pedido de uma só vez a partir desta AppDomain ... Eu não posso saber se esse for o caso , ou se foi apenas acidental.
Marc Gravell

Respostas:

177

Não é "muito comum usar um objeto somente leitura estático privado para bloqueio em multithreading" - em vez disso, é comum usar um bloqueio na granularidade apropriada / escolhida . Às vezes sim static. Mais frequentemente, IMO, não é - mas é baseado em instância .

O principal momento em que você vê um staticbloqueio é para um cache global ou para o carregamento adiado de dados globais / singletons. E no último, existem maneiras melhores de fazer isso de qualquer maneira .

Então realmente depende: como é Lockerusado no seu cenário? Trata-se de proteger algo que é em si estática? Nesse caso, o bloqueio deve ser estático. Se estiver protegendo algo que é baseado em instância , o bloqueio IMO também deve ser baseado em instância.

Marc Gravell
fonte
24
Você poderia dar mais detalhes sobre a melhor maneira de adiar o carregamento de dados globais?
bizi
Eu sempre uso um estático / volátil porque se houver vários lugares onde ele é baseado em instância, eu ainda gostaria de controlar meu método / variável sendo acessado de uma maneira segura para thread. Muitas instâncias podem estar acessando os mesmos recursos e quero controlar isso. eu também gostaria de ver o melhor nisso. Você tem uma ótima reputação e tenho certeza de que sua resposta será igualmente excelente para eu adotar. Por favor, responda?
Andrew Simpson
82

Ele não tem que ser estático, na verdade, por vezes, ele deve não ser estática.

A variável deve residir no mesmo escopo dos métodos onde você a usa para bloqueio. Se os métodos são estáticos, a variável deve ser estática, e se os métodos são métodos de instância, a variável deve ser uma variável de instância.

Uma variável estática ainda funcionará quando usada para bloquear em um método de instância, mas você estará bloqueando demais. Você bloqueará todos os métodos em todas as instâncias, não apenas os métodos na mesma instância.

Guffa
fonte
28
+1 para o "a-ha" ... Você bloqueará todos os métodos em todas as instâncias, não apenas os métodos na mesma instância.
radarbob
3
@radarbob - Pequeno detalhe: Você não bloqueia todos os métodos, apenas tira um bloqueio no qual mais clientes possam estar interessados. Os métodos nunca são bloqueados, apenas o mutex foi levado.
Erno,
Suspeito que a formulação desta resposta pode ser enganosa - o bloqueio não deve ter nada a ver com o escopo dos métodos - ele deve se preocupar apenas com o escopo dos dados compartilhados acessados ​​nesses métodos. O método de instância não pode acessar quaisquer dados compartilhados (e, portanto, não há necessidade de bloqueio), pode acessar dados compartilhados estáticos (e, portanto, precisa de bloqueio estático, refatorar também pode ser uma boa ideia), o mesmo para estático ...
Alexei Levenkov
@AlexeiLevenkov: Você está certo ao dizer que o escopo deve ser decidido se os dados são estáticos ou não, mas o escopo dos métodos também deve ser decidido por isso, para que tudo se encaixe. Os dados da instância normalmente não precisam de bloqueio, mas se a instância for compartilhada entre threads, você precisará de bloqueio.
Guffa
28

O escopo e a vida útil de uma fechadura podem / devem depender da 'coisa' que você deseja bloquear. Os bloqueios estáticos são usados ​​principalmente para bloquear coisas estáticas.

ER não
fonte
3
Detalhe menor: a fechadura não é estática, o objeto que você usa para identificar a fechadura é estático. Outro pequeno detalhe: você não bloqueia "coisas".
Guffa,
2
Sim, acho que se tentarmos trancar as "coisas" erradas, elas podem ser grandes e fortes demais e um dia escapar.
ProfK
12
@Guffa Isso é estranho, em um comentário acima você disse corretamente: "Você está complicando demais as coisas", agora vejo 1 minuto antes de dizer isso, parece que você complicou demais as coisas :)
Nicholas Petersen