代码之家  ›  专栏  ›  技术社区  ›  Erik Funkenbusch

锁定对象

  •  5
  • Erik Funkenbusch  · 技术社区  · 14 年前

    我经常看到这样的代码 shown here ,即分配一个对象,然后用作“锁定对象”。

    在我看来,您可以为此使用任何对象,包括事件本身作为锁对象。为什么要分配一个不做任何事情的新对象?我的理解是,在一个对象上调用lock()并不会实际改变对象本身,也不会实际锁定它使其不被使用,它只是用作多个要锚定的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;
                }
            }
        }
    }
    

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

    3 回复  |  直到 14 年前
        1
  •  7
  •   Marc Gravell    14 年前

    包括事件本身

    不,你不能那样做。“event”实际上只是一些访问器方法。假设您是指支持委托,那将是非常糟糕的-委托是不可变的:每次添加/删除订阅服务器时,您都会得到一个 不同的 代表。

    实际上,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; }
        }
    }
    

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

        2
  •  2
  •   Hans Passant    14 年前

    任何私有引用类型成员都将执行该作业。只要它是私有的,永远不会被重新分配。它会使委托对象退出运行, 一定地 不希望仅仅因为不受控制的客户机代码分配了一个事件处理程序而导致锁无法完成其工作。非常难调试。

    使用做其他工作的私有成员并不能很好地扩展。如果发现重构或调试时需要锁定另一个代码区域,则需要找到另一个私有成员。这就是事情很快就会变坏的地方:你可以选择 相同的 又是私人会员。死锁敲门。

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

        3
  •  1
  •   Darin Dimitrov    14 年前

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