何时在C#中使用Monitor
类或lock
关键字来确保线程安全?
修改
从目前为止的答案看来,lock
是对Monitor
类的一系列调用的简写。锁定电话到底是什么?或者更明确地说,
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 ?
}
}
更新
谢谢大家的帮助:我已经发布了另一个问题,作为您提供的一些信息的后续跟进。由于您似乎精通这一领域,我已发布链接:What is wrong with this solution to locking and managing locked exceptions?
答案 0 :(得分:80)
Eric Lippert在他的博客中谈到这一点: Locks and exceptions do not mix
C#4.0和早期版本之间的等效代码不同。
在C#4.0中它是:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
{ body }
}
finally
{
if (lockWasTaken) Monitor.Exit(temp);
}
它取决于Monitor.Enter
在锁定时原子设置标志。
早些时候是:
var temp = obj;
Monitor.Enter(temp);
try
{
body
}
finally
{
Monitor.Exit(temp);
}
这依赖于在Monitor.Enter
和try
之间抛出异常。我认为在调试代码中违反了这种情况,因为编译器在它们之间插入了一个NOP,从而使线程在这些可能的情况下中止。
答案 1 :(得分:37)
lock
只是Monitor.Enter
+ try
和finally
的{{1}}的快捷方式。只要它足够就使用lock语句 - 如果你需要像TryEnter这样的东西,你将不得不使用Monitor。
答案 2 :(得分:20)
锁定语句相当于:
Monitor.Enter(object);
try
{
// Your code here...
}
finally
{
Monitor.Exit(object);
}
但是,请记住,Monitor也可以 Wait()和 Pulse(),这在复杂的多线程情况下通常很有用。
<强>更新强>
然而,在C#4中,它的实现方式不同:
bool lockWasTaken = false;
var temp = obj;
try
{
Monitor.Enter(temp, ref lockWasTaken);
//your code
}
finally
{
if (lockWasTaken)
Monitor.Exit(temp);
}
Thanx到CodeInChaos征求意见和links
答案 3 :(得分:6)
正如其他人所说,lock
是&#34;等同于&#34;到
Monitor.Enter(object);
try
{
// Your code here...
}
finally
{
Monitor.Exit(object);
}
但出于好奇,lock
将保留您传递给它的第一个引用,如果您更改它将不会抛出。 我知道不建议更改锁定的对象而您不想这样做。
但同样,对于科学来说,这很好用:
var lockObject = "";
var tasks = new List<Task>();
for (var i = 0; i < 10; i++)
tasks.Add(Task.Run(() =>
{
Thread.Sleep(250);
lock (lockObject)
{
lockObject += "x";
}
}));
Task.WaitAll(tasks.ToArray());
......而这不是:
var lockObject = "";
var tasks = new List<Task>();
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());
错误:
类型的异常&#39; System.Threading.SynchronizationLockException&#39; 发生在70783sTUDIES.exe但未在用户代码中处理
附加信息:调用了对象同步方法 一个不同步的代码块。
这是因为Monitor.Exit(lockObject);
会对lockObject
采取行动,因为strings
是不可变的,所以已经改变了,然后你从一个不同步的代码块中调用它..但无论如何。这只是一个有趣的事实。
答案 4 :(得分:5)
Monitor
更灵活。对我来说,使用显示器时最喜欢的情况是当你不希望等待轮到你时,只需跳过:
//already executing? eff it, lets move on
if(Monitor.TryEnter(_lockObject))
{
//do stuff;
Monitor.Exit(_lockObject);
}
答案 5 :(得分:4)
两者都是一回事。 lock是c sharp关键字并使用Monitor类。
http://msdn.microsoft.com/en-us/library/ms173179(v=vs.80).aspx
答案 6 :(得分:3)
锁定和显示器的基本行为(输入+退出)或多或少相同,但显示器有更多选项,可以让您更多同步。
锁是一种快捷方式,它是基本用法的选项。
如果您需要更多控制,显示器是更好的选择。您可以使用Wait,TryEnter和Pulse来获得高级用法(如障碍,信号量等)。
答案 7 :(得分:1)
锁定 Lock关键字可确保一个线程一次执行一段代码。
lock(lockObject)
{
// Body
}
lock关键字通过获取给定对象的互斥锁,执行一条语句然后释放该锁,将语句块标记为关键部分
如果另一个线程试图输入锁定的代码,它将等待,阻塞直到对象被释放。
监视器 Monitor是一个静态类,属于System.Threading命名空间。
它提供了对对象的排他锁,因此在任何给定时间点只有一个线程可以进入关键部分。
Monitor和C#锁定之间的区别
锁定是Monitor的快捷方式,请尝试并最终输入。 锁柄尝试并最终在内部阻塞 Lock = Monitor +再试一次。
如果您想要更多控制权来使用TryEnter()
Wait()
,Pulse()
和PulseAll()
方法来实现高级多线程解决方案,那么Monitor类是您的选择。
C#Monitor.wait()
:一个线程等待其他线程通知。
Monitor.pulse()
:一个线程通知另一个线程。
Monitor.pulseAll()
:一个线程通知进程中的所有其他线程
答案 8 :(得分:0)
除了上述所有说明之外,lock是C#语句,而Monitor是System.Threading命名空间中的.NET类。