【问题标题】:Synchronize at most derived destructor最多同步派生的析构函数
【发布时间】:2017-11-14 08:53:43
【问题描述】:

我在调用纯虚方法时遇到了崩溃,这是由于其他线程在派生类已经销毁时仍在调用它的竞争条件。这是它的要点:

class Resource
{
protected:
    Resource();
    virtual ~Resource();

public:
    virtual void *lock_shared() = 0;
    virtual void unlock_shared() = 0;

    // Wait for all other threads to finish.
    void sync()
    {
        mutex.lock();
        mutex.unlock();
    }

protected:
    std::shared_mutex mutex;
};

Resource::~Resource()
{
    sync();
}

class Image : public Resource
{
public:
    Image();
    ~Image() override;

    void *lock_shared() override
    {
        mutex.lock_shared();

        return accessData();
    }

    void unlock_shared() override
    {
        processData();

        mutex.unlock_shared();
    }
};

请注意,当 Image 类型的对象被销毁时,其目的是等待所有具有共享访问权限的线程完成。但是,由于 C++ 析构函数的调用顺序,Image::~Image() 是在我们 sync() 进入 Resource::~Resource 时完成的,这意味着对象不再是 Image 类型,我们不能调用任何 Image的方法。然而,其他仍持有锁的线程将在完成后尝试调用Image::unlock(),从而导致对Resource::unlock() 的纯虚拟调用并中止程序。

明显的解决方案很简单:改为在Image::~Image() 中调用sync()

不幸的是,每当我从ResourceImage 派生新类时,这种情况很容易再次发生。我已将assert() 添加到Resource::~Resource() 以检查try_lock() 是否始终成功,但是当我从Image 派生时,这无济于事。

所以我想知道是否有人知道更简单的方法来一劳永逸地防止这种竞争状况。谢谢。

【问题讨论】:

  • 仅仅锁定和解锁互斥锁永远不会给你线程安全。另一个线程可能在解锁之后但在函数返回/对象被销毁之前进入。为了安全起见,您必须在对象的整个销毁过程中(即在对象之外)持有互斥锁。
  • 这听起来很脆弱。一个漂亮的std::shared_ptr 和一个除对象破坏之外的方法来发出退出信号怎么样?

标签: c++ multithreading destructor


【解决方案1】:

作为一个想法,如果您可以使用某些工厂来创建派生自 Resource 的类的对象,而不是显式创建它们,您可以使用自定义删除器使该工厂返回 std::unique_ptr<DerivedResource>,该删除器之前会调用 p_obj->sync()实际上删除了一个拥有的 p_obj 实例。

【讨论】:

  • 在我看来,工厂应该总是返回std::unique_ptr。如果这就是调用者需要的全部,那么它很好而且高效。如果调用者需要共享所有权,她总是可以将返回值存储在自己的std::shared_ptr 中并“升级”到共享所有权。工厂没有理由强制其用户共享所有权(以及暗示的开销)。
  • 谢谢你的评论,我完全同意你的观点。
  • 很高兴我们同意 - 我看到您更新了您的答案,不再返回 std::shared_ptr +1 :)
  • shared_ptr 是我与“自定义删除器”的默认关联,而不是工厂的有意识选择)再次感谢您指出这个细节。
  • 即使我在一般情况下同意,请注意使用std::make_sharedstd::make_unique 进行(微)优化转换为std::shared_ptr
猜你喜欢
  • 2017-10-08
  • 2014-05-17
  • 2013-03-30
  • 2015-04-16
  • 2014-08-08
  • 1970-01-01
  • 2020-09-29
  • 2021-03-20
  • 2011-11-16
相关资源
最近更新 更多