锁定非静态字段有什么问题?锁定特定实例的正确方法是什么?

sma*_*man 9 c# multithreading deadlock locking thread-safety

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

并且,如果我没有锁定非静态字段,那么如何锁定实例方法而不将该方法锁定在相同或派生类的所有其他实例上?

我写了一个例子来使我的问题更清楚.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

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

更新

谢谢大家的帮助:我发布了另一个问题,作为您提供的一些信息的后续跟进.由于您似乎精通这一领域,我发布了链接:此锁定和管理锁定异常的解决方案有什么问题?

Bru*_*ant 13

这不是关于它的坏习惯,而是关于你的目的是什么.

静态字段被访问(或"共同")该类型的所有实例.因此,锁定这样的静态字段使您能够控制该类型的所有实例之间的并发性,或者,实现的并发控制范围是该类型的所有实例.

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


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

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

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

现在,另一件事:通常,对于1,您将拥有一个包裹该资源的类.并且,通常您将该类设计为单例.现在,对于单身人士来说,这很有趣:根据设计,您可以保证只有一个实例,因此无论您是锁定实例还是静态字段都无关紧要.

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


Yoc*_*mer 5

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

其区别在于锁所在的位置(或其可访问性)。
如果将其作为静态成员,则同一类的所有对象都可以访问它。所以你得到一把锁,这将锁定所有的东西。

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

在这种情况下,没有好的或坏的做法。这只是一个你想要实现什么目标的问题。

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