我经常看到类似shown here的代码,即分配对象然后用作“锁定对象”。
在我看来,你可以使用任何对象,包括事件本身作为锁对象。为什么要分配一个什么都不做的新对象?我的理解是在对象上调用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;
}
}
}
}
所以我的问题是,这真的是一件好事吗?
答案 0 :(得分:7)
包括活动本身
不,你不能这样做。 “事件”实际上只是一些存取方法。假设您的意思是支持委托,那将是非常糟糕的 - 委托是不可变的:每次添加/删除订阅者时,您都会获得不同的委托。
实际上,4.0编译器现在使用Interlocked
使用无锁代码执行此操作 - 可能值得采用这种方法。
在您的示例中,objectLock
确保所有调用者(对于该实例)都锁定相同的对象,这很重要 - 但没有锁定{{1 (这是C#编译器使用工作的方式)。
-
更新:您的示例显示了在C#4.0之前必需的代码,访问类似字段的事件内部直接与字段交谈的类型:正常的字段式事件锁定不是尊重。这在C#4.0中有所改变;你现在可以(在C#4.0中)安全地重写为:
this
然后遵循所有正确的行为。
答案 1 :(得分:2)
任何私人引用类型成员都可以完成这项工作。只要它是私有的,永远不会被重新分配。哪个将委托对象从运行中敲出,你肯定不希望看到锁无法完成其工作只是因为你不控制的客户端代码分配了一个事件处理程序。非常难以调试。
使用执行其他工作的私人成员并不能很好地扩展。如果您发现在重构或调试时需要锁定另一个代码区域,则需要找到另一个私有成员。事情可能会迅速恶化:您可能会再次选择相同的私人会员。死锁敲门。
如果将锁定对象专用于需要保护的特定共享变量集,则不会发生这种情况。也允许你给它一个好名字。
答案 2 :(得分:1)
建议锁定私有静态字段,因为这可以确保阻止多个并行尝试访问锁的线程。锁定类本身的实例(lock(this)
)或某些实例字段可能会有问题,因为如果两个线程在对象的两个不同实例上调用该方法,则它们将能够同时输入lock语句。