Acabei de perceber que, em algum lugar do meu código, tenho a declaração de retorno dentro da fechadura e algumas vezes fora. Qual é o melhor?
1)
void example()
{
lock (mutex)
{
//...
}
return myData;
}
2)
void example()
{
lock (mutex)
{
//...
return myData;
}
}
Qual devo usar?
c#
.net
multithreading
mutex
Patrick Desjardins
fonte
fonte
Respostas:
Essencialmente, o que torna o código mais simples. Um único ponto de saída é um ótimo ideal, mas eu não alteraria o código apenas para alcançá-lo ... E se a alternativa estiver declarando uma variável local (fora do bloqueio), inicializando-o (dentro do bloqueio) e depois devolvê-lo (fora da fechadura), então eu diria que um simples "retorno" dentro da fechadura é muito mais simples.
Para mostrar a diferença na IL, vamos codificar:
(note que felizmente argumentaria que
ReturnInside
é um bit mais simples / limpo de C #)E olhe para o IL (modo de lançamento etc):
Então, no nível de IL, eles são [dar ou receber alguns nomes] idênticos (eu aprendi alguma coisa ;-p). Como tal, a única comparação sensata é a lei (altamente subjetiva) do estilo de codificação local ... Prefiro
ReturnInside
a simplicidade, mas também não me empolguei.fonte
ret
dentro de uma.try
região.Não faz diferença; ambos são traduzidos para a mesma coisa pelo compilador.
Para esclarecer, qualquer um é efetivamente traduzido para algo com a seguinte semântica:
fonte
Eu definitivamente colocaria o retorno dentro da fechadura. Caso contrário, você corre o risco de outro encadeamento entrar no bloqueio e modificar sua variável antes da instrução de retorno, fazendo com que o chamador original receba um valor diferente do esperado.
fonte
Depende,
Eu vou contra a corrente aqui. Eu geralmente voltaria para dentro da fechadura.
Normalmente, a variável mydata é uma variável local. Gosto de declarar variáveis locais enquanto as inicializo. Eu raramente tenho os dados para inicializar meu valor de retorno fora do meu bloqueio.
Portanto, sua comparação é realmente falha. Embora, idealmente, a diferença entre as duas opções seja a que você escreveu, o que parece indicar o caso 1, na prática é um pouco mais feio.
vs.
Acho que o caso 2 é consideravelmente mais fácil de ler e mais difícil de estragar, especialmente para trechos curtos.
fonte
Se o bloqueio externo parecer melhor, mas tome cuidado se você alterar o código para:
Se f () precisar ser chamado com a trava mantida, obviamente ela deverá estar dentro da trava, pois assim, manter retornos dentro da trava para obter consistência faz sentido.
fonte
Quanto vale a pena, a documentação no MSDN tem um exemplo de retorno de dentro do bloqueio. A partir das outras respostas aqui, parece ser uma IL muito semelhante, mas, para mim, parece mais seguro retornar de dentro do bloqueio, porque você não corre o risco de uma variável de retorno ser substituída por outro encadeamento.
fonte
Para facilitar a leitura do código por outros desenvolvedores, sugiro a primeira alternativa.
fonte
lock() return <expression>
declarações sempre:1) digite bloqueio
2) torna o armazenamento local (seguro para threads) para o valor do tipo especificado,
3) preenche a loja com o valor retornado por
<expression>
,4) trava de saída
5) devolva a loja.
Isso significa que o valor, retornado da instrução lock, sempre "cozinhado" antes do retorno.
Não se preocupe
lock() return
, não ouça ninguém aqui))fonte
Nota: Acredito que esta resposta seja factualmente correta e espero que seja útil também, mas estou sempre feliz em melhorá-la com base em comentários concretos.
Para resumir e complementar as respostas existentes:
A resposta aceita mostra que, independentemente da forma de sintaxe que você escolher no código C # , no código IL - e, portanto, no tempo de execução -,
return
isso não acontecerá até depois que o bloqueio for liberado.return
interior dolock
bloco represente incorretamente o fluxo de controle [1] , é sintaticamente conveniente , uma vez que evita a necessidade de armazenar o valor de retorno em um aux. variável local (declarada fora do bloco, para que possa ser usada com umareturn
fora do bloco) - veja a resposta de Edward KMETT .Separadamente - e esse aspecto é incidental à pergunta, mas ainda pode ser interessante ( a resposta de Ricardo Villamil tenta abordá-la, mas incorretamente, eu acho) - combinando uma
lock
afirmação com umareturn
afirmação - ou seja, obtendo valorreturn
em um bloco protegido de acesso simultâneo - apenas "protege" significativamente o valor retornado no escopo do chamador se ele não precisar de proteção uma vez obtido , o que se aplica nos seguintes cenários:Se o valor retornado for um elemento de uma coleção que precisa apenas de proteção em termos de adição e remoção de elementos, não em termos de modificações dos próprios elementos e / ou ...
... se o valor que está sendo retornado for uma instância de um tipo ou sequência de valores .
Em qualquer outro caso, o bloqueio deve ser realizado pelo chamador , não (apenas) dentro do método.
[1] Theodor Zoulias aponta que isso é tecnicamente também é verdade para a colocação
return
dentrotry
,catch
,using
,if
,while
,for
, ... declarações; no entanto, é o objetivo específico dalock
declaração que provavelmente convidará ao exame do verdadeiro fluxo de controle, conforme evidenciado por essa pergunta ter sido feita e ter recebido muita atenção.[2] O acesso a uma instância de tipo de valor invariavelmente cria uma cópia local da thread e na pilha; mesmo que as strings sejam tecnicamente instâncias do tipo referência, elas se comportam efetivamente com instâncias do mesmo valor.
fonte
lock
, e derivando significado do posicionamento da declaração de retorno. Qual é uma discussão não relacionada a esta pergunta IMHO. Também acho o uso de "deturpa" bastante perturbador. Se retornar de umalock
deturpa o fluxo de controle, em seguida, o mesmo poderia ser dito para retornar de umatry
,catch
,using
,if
,while
,for
, e qualquer outra construção da linguagem. É como dizer que o C # está cheio de deturpações de fluxo de controle. Jesus ...try
,if
... Eu, pessoalmente, não tendem a sequer pensar nisso, mas no contexto delock
, especificamente, surgiu a questão para mim - e se ele não surgiu também para os outros, esta questão nunca teria sido solicitado , e a resposta aceita não teria se esforçado muito para investigar o verdadeiro comportamento.