Em um aplicativo de encadeamento c #, se eu bloquear um objeto, digamos uma fila, e se ocorrer uma exceção, o objeto permanecerá bloqueado? Aqui está o pseudocódigo:
int ii;
lock(MyQueue)
{
MyClass LclClass = (MyClass)MyQueue.Dequeue();
try
{
ii = int.parse(LclClass.SomeString);
}
catch
{
MessageBox.Show("Error parsing string");
}
}
Pelo que entendi, o código após a captura não é executado - mas estou me perguntando se o bloqueio será liberado.
Respostas:
Primeiro; você considerou o TryParse?
in li; if(int.TryParse(LclClass.SomeString, out li)) { // li is now assigned } else { // input string is dodgy }
O bloqueio será liberado por 2 motivos; primeiro,
lock
é essencialmente:Monitor.Enter(lockObj); try { // ... } finally { Monitor.Exit(lockObj); }
Segundo; você captura e não re-lança a exceção interna, de modo que
lock
nunca realmente veja uma exceção. Claro, você está segurando o bloqueio durante a MessageBox, o que pode ser um problema.Portanto, ele será lançado em todas as exceções irrecuperáveis, exceto nas mais fatais e catastróficas.
fonte
TheadAbortException
ocorrer um entreMonitor.Enter
etry
: blogs.msdn.com/ericlippert/archive/2009/03/06/…Observo que ninguém mencionou em suas respostas a esta velha questão que liberar um bloqueio em uma exceção é uma coisa incrivelmente perigosa de se fazer. Sim, as instruções de bloqueio em C # têm semântica "finalmente"; quando o controle sai do bloqueio normal ou anormalmente, o bloqueio é liberado. Vocês todos estão falando sobre isso como se fosse uma coisa boa, mas é uma coisa ruim! A coisa certa a fazer se você tiver uma região bloqueada que lança uma exceção não tratada é encerrar o processo doente imediatamente antes que destrua mais dados do usuário , e não liberar o bloqueio e continuar .
Veja desta forma: suponha que você tenha um banheiro com uma fechadura na porta e uma fila de pessoas esperando do lado de fora. Uma bomba explodiu no banheiro, matando a pessoa lá dentro. Sua pergunta é "nessa situação, a fechadura será desbloqueada automaticamente para que a próxima pessoa possa entrar no banheiro?" Sim vai. Isso não é uma coisa boa. Uma bomba explodiu lá e matou alguém! O encanamento provavelmente está destruído, a casa não é mais estruturalmente sólida e pode haver outra bomba lá . A coisa certa a fazer é tirar todos o mais rápido possível e demolir a casa inteira.
Quero dizer, pense bem: se você bloqueou uma região do código para ler de uma estrutura de dados sem sofrer mutação em outro encadeamento e algo nessa estrutura de dados lançou uma exceção, as chances são boas de que é porque a estrutura de dados está corrompido . Os dados do usuário agora estão confusos; você não deseja tentar salvar os dados do usuário neste momento porque, então, estará salvando dados corrompidos . Basta encerrar o processo.
Se você bloqueou uma região do código para realizar uma mutação sem outro thread lendo o estado ao mesmo tempo, e a mutação é lançada, então se os dados não estavam corrompidos antes, com certeza está agora . Qual é exatamente o cenário contra o qual a fechadura deve proteger . Agora, o código que está esperando para ler esse estado receberá imediatamente acesso ao estado corrompido e provavelmente travará. Novamente, a coisa certa a fazer é encerrar o processo.
Não importa como você o faça, uma exceção dentro de uma fechadura é uma má notícia . A pergunta certa a fazer não é "meu bloqueio será limpo no caso de uma exceção?" A pergunta certa a se fazer é "como posso garantir que nunca haja uma exceção dentro de um bloqueio? E, se houver, como faço para estruturar meu programa para que as mutações sejam revertidas para o estado normal anterior?"
fonte
sim, isso vai liberar corretamente;
lock
atua comotry
/finally
, com oMonitor.Exit(myLock)
em finalmente, portanto, não importa como você saia, ele será liberado. Como uma observação lateral,catch(... e) {throw e;}
é melhor evitar, pois isso danifica o rastreamento da pilhae
; é melhor não pegá-lo de jeito nenhum , ou alternativamente: use emthrow;
vez dethrow e;
fazer um relance.Se você realmente deseja saber, um bloqueio em C # 4 / .NET 4 é:
{ bool haveLock = false; try { Monitor.Enter(myLock, ref haveLock); } finally { if(haveLock) Monitor.Exit(myLock); } }
fonte
"Uma instrução de bloqueio é compilada para uma chamada para Monitor.Enter e, em seguida, um bloco try… finally. No bloco finally, Monitor.Exit é chamado.
A geração de código JIT para x86 e x64 garante que um encadeamento abortado não ocorra entre uma chamada Monitor.Enter e um bloco try que a segue imediatamente. "
Retirado de: Este site
fonte
Monitor.Enter
e otry
, de modo que a condição "segue imediatamente" do JIT é violado.Seu bloqueio será liberado corretamente. A
lock
age assim:try { Monitor.Enter(myLock); // ... } finally { Monitor.Exit(myLock); }
E
finally
a execução dos blocos é garantida, não importa como você saia dotry
bloco.fonte
Só para acrescentar um pouco à excelente resposta de Marc.
Situações como essa são a própria razão da existência da
lock
palavra - chave. Isso ajuda os desenvolvedores a garantir que o bloqueio seja liberado nofinally
bloco.Se você for forçado a usar
Monitor.Enter
/Exit
por exemplo, para suportar um tempo limite, certifique-se de colocar a chamada paraMonitor.Exit
nofinally
bloco para garantir a liberação adequada do bloqueio em caso de uma exceção.fonte