【问题标题】:What exactly means locking on an object?锁定对象到底是什么意思?
【发布时间】:2010-01-01 09:00:18
【问题描述】:
尽管我在我的应用程序中使用了锁,但我不明白对特定引用类型进行锁定究竟是什么。我认为它只是停止线程,直到 {} 的内容完成。但是我已经读过锁定(this)是不好的,如果它是公开的 - 为什么?文章解释了它,但我不明白我不知道对象本身被锁定发生了什么。
例如,如果我使用 lock(this) 并从另一个线程调用它的方法怎么办?我以为只有锁下的代码受到保护,否则我将根本无法访问锁定的对象?
谢谢
【问题讨论】:
标签:
c#
multithreading
locking
【解决方案1】:
托管堆上的每个对象都可以用作锁定对象,这是一种在线程之间同步访问的方法。
我认为它只是停止线程,直到 {} 的内容完成。
好吧,lock 它会阻止 其他 线程获取锁,直到锁被释放,这最常见于 lock 语句的末尾(但它也可以是Monitor.Wait)。
lock(this) 的用法很危险,因为锁定很复杂,准确了解哪些线程在何时锁定哪些对象对于避免死锁非常重要;但是,如果您 lock(this) 您无法控制其他线程 - 也可能会(意外地)锁定同一个对象。使用private 字段进行锁定要安全得多。
例如,如果您有(在同步列表中):
private IList<T> innerList = ...
public int Count { get { lock(this) { return innerList.Count; } } }
那么不难想象另一段代码也引用了这个同步列表,并锁定它,例如:
SyncList<T> list = ...
lock(list) { // lock for atomicity
if(!list.Contains(value)) list.Add(value);
}
这是一个潜在的死锁;如果Count 没有 lock(this) 会更好,但是锁定了一个私有对象,即
private readonly object syncLock = new object();
public int Count { get { lock(syncLock) { return innerList.Count; } } }
现在没有这个问题的风险。这里的另一个问题是类似字段的事件和[MethodImpl] 都会导致lock(this)。由于完全相同的原因,锁定 Type(对于静态方法)同样危险。
【解决方案2】:
对象本身未锁定。将每个对象视为具有关联锁(或监视器)。当一个线程获得锁时,如果没有第一个线程释放它,其他线程就无法获得它,要么通过调用Monitor.Exit(这是在lock 语句的末尾发生的情况),要么通过调用Monitor.Wait。调用Monitor.Enter 获取锁的线程将阻塞,直到它可以获取锁。
对象本身根本没有“受保护” - 锁基本上是建议性的。
不锁定“this”的原因是你不知道还有哪些代码引用了“this”。获得正确的锁定要求您知道在哪些情况下线程将拥有或不拥有锁定 - 而且您无法知道您无法控制的代码是否可以取出锁定。例外情况是,如果您为了共享锁的明确目的而公开引用(例如 .NET 1.1 集合上的 SyncRoot 属性)。
【解决方案3】:
锁定一个对象对对象本身没有任何作用——它只是意味着任何其他试图锁定同一个对象的线程都将被停止,直到锁定线程释放它。
lock(this) 不受欢迎的原因是对象之外的其他代码也可以锁定该对象,从而干扰其正常运行。
【解决方案4】:
当您使用lock(someObject) { ...code... } 时,这意味着只有当线程一次可以在锁定区域内时(假设您总是锁定同一个对象!)。尝试进入该区域的第二个线程将阻塞并且必须等到第一个线程离开。如果您有两个lock (someObject) { } 区域锁定在同一个对象上,则只有一个线程可以进入这些区域中的任何一个。
锁定通常是一个需要保密的实现细节。锁定公共成员是一个坏主意,因为这意味着您的类的客户端也可以锁定同一个对象,这可能会导致死锁或不必要的减速。出于同样的原因,锁定this 是不好的。
创建一个私有的新对象并锁定它。
【解决方案5】:
using System;
using System.Threading;
class sycexp
{
public static void Main()
{
exp e=new exp();
Thread t1=new Thread(new ThreadStart(e.show));
Thread t2=new Thread(new ThreadStart(e.show));
t1.Name="First Thread";
t2.Name="Second Thread";
t1.Start();
t2.Start();
}
}
class exp
{
public object lockme=new object();
public void show()
{
lock(lockme)
{
Console.WriteLine("Start "+Thread.CurrentThread.Name.ToString());
Console.WriteLine("1");
Console.WriteLine("2");
Console.WriteLine("3");
Console.WriteLine("4");
Console.WriteLine("5");
Console.WriteLine(Thread.CurrentThread.Name.ToString()+" Stopped");
}
}
}