我有一个用于音频信号处理的有向图数据结构(如果您好奇,请参阅http://audulus.com )。
我希望图形边缘成为强引用,因此在没有循环的情况下,std::shared_ptr
可以解决问题。唉,图中可能存在循环。
所以,我有一个简单的并发标记扫描收集器的想法:
mutator 线程将事件发送到收集器线程。收集器线程维护它自己的图表示,并且不遍历 mutator 线程的图。收集器线程只是定期使用标记扫描。
事件将如下(以函数调用形式):
AddRoot(Node*)
RemoveRoot(Node*)
AddEdge(Node*, Node*)
RemoveEdge(Node*, Node*)
这个方案正确吗?收集器线程具有更改器线程所看到的旧版本。我的直觉是,由于早期无法访问的节点在以后仍然无法访问,因此收集器线程可能会在找到一个无法访问的对象时立即删除它。
此外,如果它对一个 mutator 线程是正确的,它是否适用于多个 mutator 线程?
更新
我在这里发布了代码:https ://github.com/audulus/collector 。该代码实际上是相当通用的。用于RootPtr<T>
自动跟踪根节点。节点之间的链接使用EdgePtr<T>
.
收集器似乎适用于多个 mutator 线程(在我的应用程序和单元测试中),但我觉得需要正确性证明。
请注意(针对@AaronGolden 下面的评论,从下面的评论来看,人们没有阅读此内容):mutator 线程负责以正确的顺序调用收集器函数。例如,如果 mutator 线程RemoveEdge(a,b)
在分配b
给 a之前调用RootPtr
,则收集器可能会干预并收集b
。
更新 2:
我已将代码更新到我的最新版本并更新了上面的链接。我现在已经在我的应用程序中使用了一年多的代码,并且没有将任何错误归咎于它。