我有一个由不同线程共享的全局对象。为了提供同步,我在全局对象中添加了一个互斥锁。 在访问对象内的数据之前,Mutex将被线程锁定。 一切都很好,除了删除。 如果线程正在锁定互斥锁并删除该对象。 它怎么能解锁呢? (因为内存将被释放用于数据,因此是互斥的)
如何使用这种方法实现安全删除,即将互斥锁保留在对象中?
答案 0 :(得分:3)
通常,您有一个类似的全局对象(使用std::mutex
作为示例):
std::mutex theGlobalMutex; // no pointer, no reference!
这样,对象将在程序开始运行之前初始化,并且一旦你离开程序就会自动清理它(被调用的析构函数由标准保证)。没问题 - 起初。在某些情况下,可以延迟实际初始化(参见文章中间某处提到的here),但如果在包含main函数的文件中定义全局对象,则应该安全。 / p>
替代方案,如果您真的想自己控制对象创建:
std::unique_ptr<std::mutex> the_mutex;
int main()
{
// before any theread is created:
the_mutex = new std::mutex();
// ...
return 0;
}
同样,互斥锁将自动清理,这次是通过智能指针。我假设你知道你不会改变智能指针中包含的对象,否则你会破坏对竞争条件的保护。
最后一点:
[...],我在全局对象中添加了一个互斥锁。
好的,你的互斥锁是全局对象的一部分。如果要保留线程安全性,则全局对象现在必须在程序的整个生命周期内存在(或者至少只要有多个线程在运行)。如果您无法通过程序设计来确保这一点 - 那么您需要将互斥体移出课堂!因此要么拥有如上所述的单独互斥锁,要么将互斥锁作为类的 static 成员。后一种选择再次提供了前者已经完成的自动清理。
修改强>
根据您的评论,您希望实现的是保护较大树的较小部分免受竞争条件的影响,以便节点可以独立使用,从而提供较小的锁定范围/持续时间。
使用您计划的方法,一旦您尝试修改整个树,就会遇到麻烦:想象一下,您将删除线程A中的一个节点.A获取互斥锁,但随后被中断。另一个线程B也试图锁定互斥锁,以修改有问题的对象,但是没有这样做并且必须等待。 A现在删除对象,B从现在开始对无效数据进行操作!
所以在修改时,你需要保护整棵树!如果您使用的是最新的C ++标准,则可以使用全局/静态std::shared_mutex
来保护您的树:对每个节点的读取访问都使用shared_lock保护整个树,每次写入访问(添加或删除节点) )正常锁定。