【问题标题】:What is wrong with locking non-static fields? What is the correct way to lock a particular instance?锁定非静态字段有什么问题?锁定特定实例的正确方法是什么?
【发布时间】:2011-06-26 02:44:02
【问题描述】:

为什么锁定非静态字段被认为是不好的做法?

而且,如果我不锁定非静态字段,那么如何在不锁定同一或派生类的所有其他实例的情况下锁定实例方法?

我写了一个例子来让我的问题更清楚。

public abstract class BaseClass
{

    private readonly object NonStaticLockObject = new object();
    private static readonly object StaticLockObject = new object();

    protected void DoThreadSafeAction<T>(Action<T> action)
        where T: BaseClass
    {
        var derived = this as T;
        if(derived == null)
        {
            throw new Exception();
        }
        lock(NonStaticLockObject)
        {
            action(derived);
        }
    }
}
public class DerivedClass :BaseClass
{
    private readonly Queue<object> _queue;
    public void Enqueue(object obj)
    {
        DoThreadSafeAction<DerivedClass>(x=>x._queue.Enqueue(obj));
    }
}

如果我对StaticLockObject 进行锁定,那么DoThreadSafeAction 方法将为派生自BaseClass 的所有类的所有实例锁定,这不是我想要的。我想确保当对象被锁定时,没有其他线程可以调用对象的特定实例上的方法。

更新

感谢大家的帮助:我已经发布了另一个问题,作为对你们提供的一些信息的跟进。由于您似乎精通这方面的知识,所以我发布了链接:What is wrong with this solution to locking and managing locked exceptions?

【问题讨论】:

  • 谁说锁定实例字段是不好的做法?

标签: c# multithreading locking thread-safety deadlock


【解决方案1】:

锁定实例字段也不错。有些人认为锁定实例本身很糟糕,这是通过在实例上使用同步方法隐式完成的,但这是另一回事。

如果其他类也使用实例进行锁定,则锁定实例(或“this”)可能会产生副作用。您可以确定您是否持有自己的个人锁。

【讨论】:

    【解决方案2】:

    您正在锁定一个用作锁的对象。

    不同之处在于锁包含(或其可访问性)的位置。
    如果您将它作为静态成员,则同一类的所有对象都可以访问它。所以你得到一个单一的锁,这将锁定他们。

    如果您将它作为类的成员(非静态),那么它只能由该对象访问。因此,每个对象实例都会获得一个锁。

    在这种情况下没有好坏习惯。这只是你想要实现什么的问题。

    请记住避免将 this 锁定在对象中。

    【讨论】:

      【解决方案3】:

      这不是坏习惯,而是你的目的是什么。

      静态字段被访问(或“通用”)该类型的所有实例。因此锁定这样一个静态字段可以让您控制该类型的所有实例之间的并发,或者,实现的并发控制范围是该类型的所有实例。

      但是,如果您锁定一个非静态字段,则该锁定将仅在该实例中处于活动状态,因此您只能在该实例内控制并发,或者,并发控制的范围实现的是实例。


      现在,每当锁定一个对象时,我都会这样。我同意的资源是什么?也许是数据库,也许是在我进行特定处理时无法更改的一堆实例字段等。一旦我知道我将自己锁定在什么之外,我就会检查它的范围。

      1. 如果它是我的应用程序之外的实体,那么它就是应用程序范围。必须同时锁定所有内容。
      2. 如果是一堆instance字段,那么就是实例范围。
      3. 如果是一堆静态字段,那么就是类型范围。

      因此,对于 1 和 3,请使用静态字段。对于 2,使用实例字段。

      现在,另一件事:通常,对于 1,您将有一个包装该资源的类。而且,通常你会将该类设计为singleton。现在,对于单例,这很有趣:设计保证您只有一个实例,因此锁定实例或静态字段都没有关系。

      PS.:如果你使用锁来保护单例的实例化,当然它应该是静态的。 (为什么?)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-19
        • 2015-01-25
        • 1970-01-01
        • 2011-06-25
        • 1970-01-01
        • 2019-07-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多