c++ - 在 C++ 中使用非虚拟公共(public)接口(interface)和作用域锁避免死锁

标签 c++ multithreading synchronization abstract-class

我遇到了一个令我很困扰的问题。看来我已经找到了一种很容易解决的情况,但是如果 a) 我在编程时注意力不集中,或者 b) 其他人开始实现我的接口(interface)并且不知道如何处理,那可能会导致问题这种情况。

这是我的基本设置:

我有一个抽象类,用作多种数据类型的通用接口(interface)。我采用了非虚拟公共(public)接口(interface)范例(Sutter,2001)以及范围锁定来提供一些线程安全性。一个示例接口(interface)类看起来像这样(我省略了有关作用域锁定和互斥实现的详细信息,因为我认为它们不相关):

class Foo
{
public:
    A( )
    {
        ScopedLock lock( mutex );
        aImp( );
    }
    B( )
    {
        ScopedLock lock( mutex );
        bImp( );
    }
protected:
    aImp( ) = 0;
    bImp( ) = 0;
}

然后由用户来实现 aImp 和 bImp,这就是问题所在。如果 aImp 执行一些使用 bImp 的操作,则执行此操作非常容易(并且在某种意义上几乎合乎逻辑):

class Bar
{
protected:
    aImp( )
    {
        ...
        B( );
        ...
    }
    bImp( )
    {
        ...
    }
}

僵局。当然,解决这个问题的简单方法是始终调用 protected 虚函数而不是它们的公共(public)变体(在上面的代码片段中将 B() 替换为 bImp())。但如果我犯了一个错误,我上吊似乎仍然很容易,或者更糟的是允许其他人上吊。

有没有人有办法阻止抽象类的实现者在编译时调用那些公共(public)函数,或者以其他方式帮助避免死锁解决方案?

只是为了好玩,一些互斥量允许操作以避免死锁问题。例如,如果我使用 Windows 函数 EnterCriticalSection 和 LeaveCriticalSection 实现它,就没有问题。但我宁愿避免平台特定的功能。我目前在我的作用域锁实现中使用 boost::mutex 和 boost::shared_mutex,据我所知,它并没有试图避免死锁(我认为我几乎更喜欢)。

最佳答案

使用私有(private)继承可能会解决您的问题:

class Foo
{
public:
  void A( )
    {
      ScopedLock lock( mutex );
      aImp( );
    }
  void B( )
    {
      ScopedLock lock( mutex );
      bImp( );
    }

protected:
  virtual void aImp( ) = 0;
  virtual void bImp( ) = 0;
};

class FooMiddle : private Foo
{
public:
  using Foo::aImp;
  using Foo::bImp;
};

class Bar : public FooMiddle
{
  virtual void aImpl ()
  {
    bImp ();
    B ();                   // Compile error - B is private
  }
};

私有(private)地从 Foo 派生,然后使用 FooMiddle 确保 Bar 无法访问 A 或 B。但是,bar 仍然能够覆盖 aImp 和 bImp,并且 FooMiddle 中的 using 声明意味着它们仍然可以被调用来自酒吧。

或者,有帮助但不能解决问题的选项是使用 Pimpl 模式。你最终会得到如下结果:

class FooImpl
{
public:
  virtual void aImp( ) = 0;
  virtual void bImp( ) = 0;
};

class Foo
{
public:
  void A( )
    {
      ScopedLock lock( mutex );
      m_impl->aImp( );
    }
  void B( )
    {
      ScopedLock lock( mutex );
      m_impl->bImp( );
    }

private:
  FooImpl * m_impl;
}

好处是在从 FooImpl 派生的类中,它们不再有“Foo”对象,因此不能轻易调用“A”或“B”。

关于c++ - 在 C++ 中使用非虚拟公共(public)接口(interface)和作用域锁避免死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/834865/

相关文章:

c# - 如何并行运行多个进程但在每个进程内按顺序执行

java - JCIP SafePoint - 它真的是线程安全的吗?

javascript - 如何阻塞调用函数并执行多个异步调用,然后继续处理收集到的数据?

c++ - 从文件中读取数字 C++

c++ - MinMax堆算法实现

java - JBOSS EJB 客户端重复出现 "OutOfMemoryError: unable to create new native thread"

linux - 两个进程写入一个文件,防止混合输出

java - 这是 'double check' 反模式的情况吗? (获取一个map条目,不存在则创建)

c++ - Symbian C++ - 从 .mbm 文件加载和显示图像

c++ - 首次运行后倒数功能无法正常工作