【发布时间】: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()。
不幸的是,每当我从Resource 或Image 派生新类时,这种情况很容易再次发生。我已将assert() 添加到Resource::~Resource() 以检查try_lock() 是否始终成功,但是当我从Image 派生时,这无济于事。
所以我想知道是否有人知道更简单的方法来一劳永逸地防止这种竞争状况。谢谢。
【问题讨论】:
-
仅仅锁定和解锁互斥锁永远不会给你线程安全。另一个线程可能在解锁之后但在函数返回/对象被销毁之前进入。为了安全起见,您必须在对象的整个销毁过程中(即在对象之外)持有互斥锁。
-
这听起来很脆弱。一个漂亮的
std::shared_ptr和一个除对象破坏之外的方法来发出退出信号怎么样?
标签: c++ multithreading destructor