Isso depende de como os bloqueios são implementados. Se você fizer isso como no artigo da Wikipedia, ou seja, guardar a seção crítica com um booleano por processo¹, certamente estará com problemas. Se um processo morre, ele nunca redefine sua sinalização e o outro processo volta para sempre.
Na prática, você pode proteger seu código contra várias maneiras de morrer. Por exemplo, considere esta implementação no estilo Java:
flag[1] = true;
turn = 1;
while ( flag[0] == true && turn == 1 ) { Thread.yield(); }
try {
// critical section
}
finally {
flag[1] = false;
}
Isso garantirá que o sinalizador seja redefinido, aconteça o que acontecer na seção crítica, desde que o sistema esteja lidando com o erro. Em Java, isso é verdade mesmo para estouros de pilha e heap. Portanto, a menos que o processo desapareça literalmente ( kill
², falha do processador, desconexão da rede, ...) você estará seguro. Observe que a maioria dos softwares não críticos falha nesses casos - como ele pode lidar com um erro que não está sendo executado? - para que seja aceito em muitos casos. Você pode lidar com inconsistências na reinicialização, se necessário.
Se você usar bloqueios adequados no nível do idioma, o sistema de tempo de execução poderá lidar com os proprietários de bloqueios desaparecidos, ou seja, liberar bloqueios com proprietários mortos. Você pode simular isso dando a cada processo um interruptor de homem morto que os outros podem ler ou verificar diretamente se o processo de propriedade da trava ainda está ativo (se o sistema suportar).
- Isso não escala bem, de qualquer maneira.
- Em Java, acho que
finalize
deveria ser executado mesmo kill
, mas isso não é garantido pelas especificações. kill -9
é provavelmente uma sentença de morte para qualquer solução que exija que o processo de morrer faça alguma coisa.