C++ 在非垃圾收集的上下文中,功能性/不可变的数据结构对并发仍然有用吗?

C++ 在非垃圾收集的上下文中,功能性/不可变的数据结构对并发仍然有用吗?,c++,data-structures,concurrency,functional-programming,immutability,C++,Data Structures,Concurrency,Functional Programming,Immutability,不可变数据结构的卖点之一是它们可以自动并行化。如果没有发生变异,那么可以在线程之间传递对函数数据结构的引用,而无需任何锁定 我开始思考如何在C++中实现功能性数据结构。假设我们在数据结构的每个节点上都有一个引用计数。(功能数据结构在数据结构的旧成员和更新成员之间共享结构,因此节点不会唯一地属于一个特定的数据结构。) 问题是,如果在不同的线程中更新引用计数,那么我们的数据结构就不再是线程安全的。将互斥体附加到每个节点既昂贵又不符合使用不可变数据结构实现并发的目的 有没有一种方法可以使并发不可变的数

不可变数据结构的卖点之一是它们可以自动并行化。如果没有发生变异,那么可以在线程之间传递对函数数据结构的引用,而无需任何锁定

我开始思考如何在C++中实现功能性数据结构。假设我们在数据结构的每个节点上都有一个引用计数。(功能数据结构在数据结构的旧成员和更新成员之间共享结构,因此节点不会唯一地属于一个特定的数据结构。)

问题是,如果在不同的线程中更新引用计数,那么我们的数据结构就不再是线程安全的。将互斥体附加到每个节点既昂贵又不符合使用不可变数据结构实现并发的目的


有没有一种方法可以使并发不可变的数据结构在C++(以及其他非垃圾收集环境)中工作?

< p>嗯,垃圾收集语言也有多线程环境的问题(并且对于可变结构来说不容易)。
您已经忘记了,与任意数据不同,计数器可以原子地递增和递减,因此互斥是不必要的。这仍然意味着需要维护处理器之间的高速缓存同步,如果单个节点不断更新,这可能会带来严重的成本。

有无锁引用计数算法,请参阅,例如


还要注意的是,C++0x(可能很快会成为C++11)包含原子整数类型,特别是在这种情况下。

我不是这方面的专家,但您可以存储每个线程的引用计数,并且只对它们进行锁定,以检查节点上的所有线程是否都达到了零引用。但我相信,有比这更为优雅的解决方案,或者一般的引用计数。这里是一个流行的不可变的C++数据结构,您可以查看<代码> SyddYPPTR <代码>的实现。这与需要有效的线程安全引用计数面临完全相同的问题。@Rob Lachlan shared_ptr引用计数已经是线程安全的。您可以查看不可变堆栈实现的线程(与垃圾收集的实现相比,它有缺点),将邋遢计数器()作为实现引用计数器的一种方法,这种计数器不会引起太多缓存争用。参考文献还提到了其他几种方法。@Karmastan:谢谢你的文章!我不确定它是否会起作用,尽管根据文章“这个操作很昂贵,所以邋遢的计数器应该只用于相对不经常取消分配的对象”,但这个想法本身很有趣。我读过的另一项技术是将计数器与对象分开,并使用TLS队列对其进行单线程更新,以缓冲inc/dec操作。我不确定我是否在这里,但使用引用计数不会将性能从O(log n)降低到O(n),因为您必须更新所有节点的引用计数,所以新创建的集合的“后代”将重用这些节点?我想,对于大多数用例来说,这违背了不可变集合的目的。但是如果我错了,请纠正我@le_me我不知道Rob想要什么样的数据结构,所以我不能说它的时间特性。可以是O(logn),可以是O(n^3),可以是O(exp(n))或者其他任何东西。引用计数的效果需要进行第二次分析,具体取决于使用模式。此外,拥有参考计数是他的想法,而不是我的。当然,我可能回答了XY问题的错误部分,但我仍然认为您的评论最好放在建议参考计数的问题上。我只是想为您提出的具体解决方案添加信息。我认为需要注意的是,引用计数的性能损失可能比集合本身的时间复杂度更高。对于任何操作,都不超过。因此,在这种情况下,引用计数将严重影响大型数据集的性能。@le_me我唯一不明白的是,您认为我是从哪里开始进行引用计数的。这是问题所在。是的,他们把它作为一个例子,但问题是如何在C++中实现不可变的数据结构。您说存在无锁引用计数算法,但我认为这不是一个好答案,因为它对性能影响太大。