锁定一个对象

我经常看到这里显示的代码,即分配对象的位置,然后用作“锁定对象”。

在我看来,你可以使用任何对象,包括事件本身作为锁对象。 为什么要分配一个什么都不做的新对象? 我的理解是,在对象上调用lock()实际上不会改变对象本身,也不会实际锁定它被使用,它只是用作多个锁定语句锚定的占位符。

public class Shape : IDrawingObject, IShape { // Create an event for each interface event event EventHandler PreDrawEvent; event EventHandler PostDrawEvent; object objectLock = new Object(); // Explicit interface implementation required. // Associate IDrawingObject's event with // PreDrawEvent event EventHandler IDrawingObject.OnDraw { add { lock (objectLock) { PreDrawEvent += value; } } remove { lock (objectLock) { PreDrawEvent -= value; } } } } 

所以我的问题是,这真的是一件好事吗?

包括事件本身

不,你做不到。 “事件”实际上只是一些访问方法。 假设您的意思是支持委托,那将是非常糟糕的 – 委托是不可变的:每次添加/删除订阅者时,您都会获得不同的委托。

实际上,4.0编译器现在使用Interlocked使用无锁代码执行此操作 – 可能值得遵循此方法。

在您的示例中, objectLock确保所有调用者(对于该实例)都锁定同一个对象,这很重要 – 但没有this进行锁定的丑陋(这是C#编译器以前的工作方式)。

更新:您的示例显示了在C#4.0之前必需的代码,访问类型的类似字段的事件直接与字段对话:不遵守正常的字段类事件锁定。 这在C#4.0中有所改变; 你现在可以(在C#4.0中) 安全地重写这个:

 public class Shape : IDrawingObject, IShape { // Create an event for each interface event event EventHandler PreDrawEvent; event EventHandler PostDrawEvent; event EventHandler IDrawingObject.OnDraw { add { PreDrawEvent += value; } remove { PreDrawEvent -= value; } } } 

然后遵循所有正确的行为。

任何私有引用类型成员都将完成这项工作。 只要它是私有的,永远不会被重新分配。 将一个委托对象从运行中敲出来,你肯定不希望看到一个锁无法完成它的工作,因为你不能控制的客户端代码会分配一​​个事件处理程序。 非常难以调试。

使用从事另一项工作的私人成员并不能很好地扩展。 如果您发现在重构或调试时需要锁定另一个代码区域,则需要找到另一个私有成员。 事情可能会迅速恶化:你可能会再次选择同一个私人会员。 死锁敲门。

如果将锁定对象专用于需要保护的特定共享变量集,则不会发生这种情况。 也允许你给它一个好名字。

建议锁定私有静态字段,因为这可以确保阻止多个并行尝试访问锁的线程。 锁定类本身的实例( lock(this) )或某些实例字段可能会有问题,因为如果两个线程在对象的两个不同实例上调用该方法,则它们将能够同时输入lock语句。