【发布时间】:2016-01-28 10:07:03
【问题描述】:
我有两个对象,A 和 B,有一个指向对方的指针。 对象 A 可能被一个线程删除,对象 B 可能被另一个线程删除。 当对象 A 或 B 被删除时,远程指针应设置为 NULL(清除双向链接)。如何以安全的方式实现这一点?
虽然描述起来很简单,但我觉得实现起来并不容易。 天真的想法是用互斥锁保护每个指针。但是为了避免死锁,两个互斥锁必须以相同的顺序锁定。这将要求一个对象首先锁定远程互斥锁。但这意味着以不受保护的方式使用指针来访问远程锁。
到目前为止,我发现的唯一解决方案是使用两个对象通过智能指针引用的一个共享互斥锁。当两个对象都被销毁时,互斥锁也被销毁。单个互斥锁保护对双向链路的访问。所以一次只能有一个线程可以修改双向链接并清除它。
有没有更好的方法?
编辑:用例详情
数据结构:
______ _____ _____ ______
| | 1 N | | 1 1 | | N 1 | |
| A' | -----> | A | <-----> | B | <----- | B' |
|______| |_____| |_____| |______|
Thread_A thread_B
A' 完全由 Thread_A 控制,B' 完全由 Thread_B 控制。 B' 和 Thread_B 在我无法更改的库中。但是 B 是用我提供给库的工厂方法构造的。所以我可以完全控制 B 的实现。
Thread_B 可以随时删除 B。因此我无法控制 B 的生命周期。Thread_A 将在删除 A' 之前关闭库。这意味着 Thread_B 在 Thread_A 删除 A' 之前删除了所有 B 和 B' 对象。 Thread_A 定期扫描 A 的列表并使用指向 B 的 NULL 指针擦除 A。
在运行时,数据必须以这种方式传输 A' -> A -> B -> B'。 Thread_A 将数据存储在队列中,thread_B 将数据从队列中拉出。我决定把队列放在B里面,因为Thread_A在队列中存储数据是一个非阻塞操作。如果队列已满,则丢弃数据。当 Thread_B 尝试从队列中提取数据时,它可能会偶然发现一个空队列。库实现者要求 Thread_B 最多等待 1 秒并返回。
所以我需要一个保护来确保清除和测试双向链接状态,并在 Thread_A 将其数据存储在 B 中的队列中时阻止删除 B。
【问题讨论】:
-
我认为
weak_ptr会是一个解决方案 -
@Slava,非常怀疑。
-
所有权我想你的意思是负责删除它。一个线程控制的一个对象拥有对象A,另一个线程控制的另一个对象拥有B。所以你有A'-> A B
-
@chmike,弱指针对你没有帮助。弱指针提供了一种获取对象的shared_ptr 的方法,并保证从同一个weak_ptr 获得的所有shared_ptr 将共享同一个控制块。它们不解决循环依赖。如果某个地方不存在 shared_ptr,weak_ptr 本身不会管理对象的生命周期。
-
@DanielStrul 我刚刚明白我不能使用weak_ptr,因为库不通过smart_ptr管理B。 B' 到 B 是一个原始指针,ThB 调用 delete B !也不需要 A 和 B 的生命周期是耦合的。我希望它解耦,这也是我创建 A 类和 B 类的原因。再次考虑它,我注意到存在不对称性。 A 在其 B 伙伴之前永远不会被删除。所以我可以将互斥锁存储在 A 中,就是这样。 Thread_A 和 Thread_B 可以锁定它并保护链路测试和修改,由 Thread_A 调用 B 中的数据排队方法并删除。
标签: c++ multithreading thread-safety