最多同步派生的析构函数

时间:2017-06-12 19:02:29

标签: c++ multithreading destructor

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

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()的纯虚拟调用并中止该程序。

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

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

所以我想知道是否有人知道一种更加万无一失的方法来一劳永逸地防止这种竞争状态。感谢。

1 个答案:

答案 0 :(得分:4)

作为一个想法,如果你可以使用一些工厂创建从Resource派生的类的对象而不是显式创建它们,你可以使用自定义删除器使这个工厂返回std::unique_ptr<DerivedResource>在实际删除p_obj->sync()的所有实例之前p_obj