使线程安全类来自抽象类

时间:2019-03-16 15:38:59

标签: c# inheritance thread-safety abstract-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
    }
}

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

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();
      }
    }
}

我可以使基本线程安全,但是我还有其他一些从同一基本继承的类,不需要线程安全,因此我不想强加线​​程安全。这种设计有什么问题吗?如果基地有很多公众成员,这有点乏味。

2 个答案:

答案 0 :(得分:2)

很难推断出代码的潜在用途,但从一般考虑的角度来看,我将重点介绍以下问题:

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

一般建议:

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

答案 1 :(得分:1)

如果要以线程安全的方式使用Class1(或该事件的后代类),则应使用封装而不是继承,如Kevin Gosse所述。不应以这种方式使用继承,因为如果Class1具有更多非虚拟的方法(甚至是公共方法),这些方法会更改对象的内部状态,则您将无法对其进行任何控制。您应该采用并封装一个继承Class1的类,然后公开将被称为线程安全方法的方法。

即使您确实控制Class1设计,每次添加或更改Class2的方法时都要想到线程安全继承者(Class1)也是一个糟糕的设计。