Bloqueios de reentrada em C #

119

O código a seguir resultará em um impasse usando o C # no .NET?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }
Cara
fonte
6
Podemos considerar alterar o título dessa pergunta - talvez para algo como o fechado recentemente Por que bloqueios aninhados não causam um impasse? Tal como está, o título parece quase projetado para impedir que as pessoas o descubram.
Jeff Sternal
12
Na verdade, achei isso com base na palavra de pesquisa 'reentrante' e ela respondeu à minha pergunta. Se é uma questão dup, isso é uma questão diferente ...
emfurry
Concordo com o comentário de @JeffSternal, esta pergunta supõe que a pessoa que está pesquisando a pergunta já esteja familiarizada com os bloqueios "Reentrante". Outra questão a duplicação Eu acho que tinha um título bom para isso: stackoverflow.com/questions/3687505/...
Luis Perez

Respostas:

148

Não, desde que você esteja travando no mesmo objeto. O código recursivo já possui efetivamente o bloqueio e, portanto, pode continuar sem impedimentos.

lock(object) {...}é uma abreviação para usar a classe Monitor . Como Marc aponta , Monitorpermite a reentrada , portanto, repetidas tentativas de bloquear um objeto no qual o thread atual já possui um bloqueio funcionarão perfeitamente.

Se você começar a travar objetos diferentes , é aí que você deve ter cuidado. Preste especial atenção a:

  • Sempre adquira bloqueios em um determinado número de objetos na mesma sequência.
  • Sempre libere os bloqueios na sequência inversa de como você os adquire.

Se você quebrar uma dessas regras, estará praticamente garantido que haverá problemas de conflito em algum momento .

Aqui está uma boa página da Web que descreve a sincronização de threads no .NET: http://dotnetdebug.net/2005/07/20/monitor-class-avoiding-deadlocks/

Além disso, prenda o menor número possível de objetos ao mesmo tempo. Considere aplicar bloqueios de granulação grossa sempre que possível. A idéia é que, se você pode escrever seu código de forma que exista um gráfico de objetos e adquirir bloqueios na raiz desse gráfico de objetos, faça-o. Isso significa que você tem um bloqueio nesse objeto raiz e, portanto, não precisa se preocupar muito com a sequência na qual você adquire / libera bloqueios.

(Mais uma observação, seu exemplo não é tecnicamente recursivo. Para ser recursivo, Bar()teria que se chamar, normalmente como parte de uma iteração.)

Neil Barnwell
fonte
1
Especialmente em diferentes seqüências.
Marc Gravell
6
Re recursão; de fato; para o benefício de Guy, o termo é re-entrantes
Marc Gravell
Obrigado pelo esclarecimento sobre a terminologia - editei e corrigi a pergunta.
24420 Guy
Essa pergunta parece chamar bastante a atenção, por isso atualizei minha resposta com algumas outras anotações que criei desde que a escrevi.
Neil Barnwell
Na verdade, não acho que a ordem na qual você libera bloqueie seja importante. A ordem em que você os escolhe definitivamente faz, mas desde que liberar o bloqueio não esteja condicionado a nada (você pode liberar a qualquer momento), você deve ficar bem desde que libere todos os bloqueios adquiridos.
bobroxsox
20

Bem, Monitorpermite a reentrada, para que você não possa entrar em conflito ... então não: não deve funcionar

Marc Gravell
fonte
7

Se um encadeamento já estiver segurando uma trava, ele não se bloqueará. A estrutura .Net garante isso. Você só precisa garantir que dois encadeamentos não tentem obter os mesmos dois bloqueios fora de sequência por quaisquer caminhos de código.

O mesmo encadeamento pode adquirir o mesmo bloqueio várias vezes, mas você deve liberar o bloqueio o mesmo número de vezes que o adquiriu. Obviamente, desde que você esteja usando a palavra-chave "lock" para fazer isso, isso acontece automaticamente.

Jeffrey L Whitledge
fonte
Observe que isso é verdade para monitores, mas não necessariamente para outros tipos de bloqueio.
Jon Skeet
(Não querendo dar a entender que você não sabia disso, é claro - apenas que é uma distinção importante :)
Jon Skeet
Este é um bom ponto. Na verdade, eu ia mudar "travar" para "monitorar" o tempo todo, mas depois me distraí. E preguiçoso. E o comportamento também é verdadeiro para objetos mutern kernal do Windows, então imaginei, perto o suficiente!
Jeffrey L Whitledge
5

Não, esse código não terá bloqueios. Se você realmente deseja criar um impasse mais simples, precisará de pelo menos 2 recursos. Considere o cenário do cão e do osso. 1. Um cão tem controle total sobre 1 osso, então qualquer outro cão tem que esperar. 2. São necessários no mínimo 2 cães com 2 ossos para criar um impasse quando prendem seus ossos respectivamente e procuram outros ossos também.

.. e assim por diante n cães e ossos e causam impasses mais sofisticados.

Rishabh Jain
fonte