监视与锁定

时间:2011-02-12 15:28:00

标签: c# .net multithreading locking monitor

何时在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?

9 个答案:

答案 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.Entertry之间抛出异常。我认为在调试代码中违反了这种情况,因为编译器在它们之间插入了一个NOP,从而使线程在这些可能的情况下中止。

答案 1 :(得分:37)

lock只是Monitor.Enter + tryfinally的{​​{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类。