Monitor vs bloqueio

Quando é apropriado usar a class Monitor ou a palavra-chave de lock para segurança de thread em C #?

EDIT: Parece a partir das respostas até agora que o lock é curto para uma série de chamadas para a class Monitor . O que exatamente é o bloqueio de mão curta para? Ou mais explicitamente,

 class LockVsMonitor { private readonly object LockObject = new object(); public void DoThreadSafeSomethingWithLock(Action action) { lock (LockObject) { action.Invoke(); } } public void DoThreadSafeSomethingWithMonitor(Action action) { // What goes here ? } } 

Atualizar

Obrigado a todos pela vossa ajuda: publiquei uma outra questão como seguimento de algumas das informações fornecidas por todas. Como você parece bem versado nessa área, publiquei o link: O que há de errado com essa solução para bloquear e gerenciar exceções bloqueadas?

Eric Lippert fala sobre isso em seu blog: bloqueios e exceções não se misturam

O código equivalente difere entre o C # 4.0 e versões anteriores.


No C # 4.0 é:

 bool lockWasTaken = false; var temp = obj; try { Monitor.Enter(temp, ref lockWasTaken); { body } } finally { if (lockWasTaken) Monitor.Exit(temp); } 

Ele se baseia em Monitor.Enter atomicamente definindo o sinalizador quando o bloqueio é tomado.


E mais cedo foi:

 var temp = obj; Monitor.Enter(temp); try { body } finally { Monitor.Exit(temp); } 

Isso depende de nenhuma exceção sendo lançada entre o Monitor.Enter e a try . Eu acho que no código de debugging esta condição foi violada porque o compilador inseriu um NOP entre eles e, assim, fez o aborto entre os possíveis.

lock é apenas um atalho para Monitor.Enter com try + finally e Monitor.Exit . Use a instrução de bloqueio sempre que for o suficiente – se precisar de algo como o TryEnter, você terá que usar o Monitor.

Uma declaração de bloqueio é equivalente a:

 Monitor.Enter(object); try { // Your code here... } finally { Monitor.Exit(object); } 

No entanto, lembre-se de que o Monitor também pode esperar () e Pulse () , que geralmente são úteis em situações complexas de multithreading.

Atualizar

No entanto, no C # 4 é implementado de forma diferente:

 bool lockWasTaken = false; var temp = obj; try { Monitor.Enter(temp, ref lockWasTaken); //your code } finally { if (lockWasTaken) Monitor.Exit(temp); } 

Thanx para CodeInChaos para comentários e links

Como outros disseram, o lock é “equivalente” a

 Monitor.Enter(object); try { // Your code here... } finally { Monitor.Exit(object); } 

Mas apenas por curiosidade, o lock preservará a primeira referência que você passar para ela e não lançará se você a alterar. Eu sei que não é recomendado alterar o object bloqueado e você não quer fazer isso.

Mas, novamente, para a ciência, isso funciona bem:

 var lockObject = ""; var tasks = new List(); for (var i = 0; i < 10; i++) tasks.Add(Task.Run(() => { Thread.Sleep(250); lock (lockObject) { lockObject += "x"; } })); Task.WaitAll(tasks.ToArray()); 

… E isso não acontece:

 var lockObject = ""; var tasks = new List(); for (var i = 0; i < 10; i++) tasks.Add(Task.Run(() => { Thread.Sleep(250); Monitor.Enter(lockObject); try { lockObject += "x"; } finally { Monitor.Exit(lockObject); } })); Task.WaitAll(tasks.ToArray()); 

Erro:

Uma exceção do tipo ‘System.Threading.SynchronizationLockException’ ocorreu em 70783sTUDIES.exe, mas não foi tratada no código do usuário

Informações adicionais: O método de synchronization de objects foi chamado de um bloco de código não sincronizado.

Isso ocorre porque Monitor.Exit(lockObject); irá atuar no lockObject que mudou porque as strings são imutáveis, então você está chamando de um bloco de código não sincronizado … mas de qualquer forma. Este é apenas um fato divertido.

Ambos são a mesma coisa. bloqueio é c palavra-chave afiada e usar class Monitor.

http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx

O bloqueio e o comportamento básico do monitor (enter + exit) é mais ou menos o mesmo, mas o monitor tem mais opções que permitem mais possibilidades de synchronization.

O bloqueio é um atalho e é a opção para o uso básico.

Se você precisar de mais controle, o monitor é a melhor opção. Você pode usar o Wait, TryEnter e o Pulse, para usos avançados (como barreiras, semáforos e assim por diante).

Monitor é mais flexível. Para mim, o caso favorito de usar o monitor é quando você não quer esperar pela sua vez, e apenas pule :

 //already executing? eff it, lets move on if(Monitor.TryEnter(_lockObject)) { //do stuff; Monitor.Exit(_lockObject); }