从抽象 class 使线程安全 class

Making a thread safe class from an abstract class

一个设计问题。我知道这个方法是线程安全的,但是从设计的角度,有没有更好的方法呢?

我有一个抽象class(不是线程安全的):

public abstract class Class1
{
    protected someobject myobject;

    public Class1()
    {
       myobject = new someoject();
    }

    public virtual void proc1()
    {
      // do something with my object
    }

   public virtual void proc2()
    {
      // do something with my object
    }

  public virtual void proc3()
    {
      // do something with my object
    }
}

现在我想从这个 class 创建一个需要线程安全的后代,所以我这样做:

public class Class2: Class1
{
    private static readonly object obj = new object();


    public override void  proc1()
    {
      lock(obj)
      {
          base.proc1();
      }
    }

   public override void proc2()
   {
      lock(obj)
      {
          base.proc2();
      }
    }

   public override void proc3()
    {
      lock(obj)
      {
          base.proc3();
      }
    }
}

我可以使基本线程安全,但我有一些其他 class 继承自相同的基本并且不需要线程安全,所以我不想强制线程安全。这样的设计有什么问题吗?如果基地有很多 public 成员,那就有点乏味了....

很难推断出您的代码的潜在用途,但从一般考虑的角度来看,我会强调以下问题:

  1. 责任。我正在查看 Class2 代码,发现与基本 class 相比,除了基本 class 方法的竞争条件保护外,它没有做任何额外的事情。通常我们倾向于对特定的状态强加线程安全,以保证并发访问条件下的状态一致性。但是在这种情况下,Class2 根本不知道它保护的行为是否会导致竞争条件。如果 Class1 以不再需要线程安全的方式修改怎么办 - 我们将在 Class2 class 中拥有冗余锁(或者将删除它们与 Class1).如果 Class1 使用其他方法扩展,或者更糟糕的是有人决定通过另一个锁对象向 Class1 添加额外的线程安全性(在最坏的情况下我们可能会出现死锁)。因此,每次我们在 Class1 中进行此类更改时,我们还必须检查 Class2 代码以确保没有任何问题,换句话说,我们在这些 class 之间紧密耦合只是因为Class2有不该有的责任。
  2. LSP。当我们谈论 classes 的层次结构时,我们通常会记住,无论使用何种类型的层次结构,层次结构合同的使用要求都不应该不同。在层次结构中线程安全和非线程安全 classes 对该层次结构的使用施加了额外的限制。特别是消费者应该知道它在什么情况下处理什么类型的实例,这可能排除了可以使用符合 LSP 的层次结构的场景数量。例如,消费者将无法使用集合Class1 在一般场景中,除非它明确知道场景是线程安全的。

一般建议:

  1. 我会尽量避免在子 classes 中引入一种行为,这可能取决于可以使用子 classes 的上下文。我会尝试使整个层次结构保持一致:层次结构中的所有 classes 都是线程安全的,或者它们都不是。
  2. 如果层次结构中的一些 classes 需要线程安全,而另一些则不需要,这可能表明层次结构契约的内聚性低。我会尝试将基础 class 和子 class 分解成更小的部分,这可能意味着多个合同和可能的层次结构。
  3. 如果基础 class 或其他 classes 保持可能在不同并发上下文中使用的状态,并且从线程安全的角度来看仍然很难实现同质层次结构,我' d 考虑将同步逻辑移到层次结构中的 classes 之外,并将此责任留给消费者。

如果您想以线程安全的方式使用 Class1(或后代 class),您应该使用封装而不是像 Kevin Gosse 所说的继承。不应该以这种方式使用继承,因为如果 Class1 有更多非虚拟的方法(甚至可能 public)会改变对象的内部状态,您将无法控制它们。您应该采用并封装一个继承 Class1 的 class,然后公开将作为线程安全方法调用的方法。

即使你做controlClass1设计,每次添加或更改Class1时都想到Thread安全继承者(Class2)也是一个糟糕的设计'方法