我已经在多个地方看到过此声明,包括SO:https://stackoverflow.com/a/20467457/243238,https://stackoverflow.com/a/4400389/243238。我认为修改数据不需要锁定,但在并发修改后最终会有多个版本。这在实践中似乎没有用。我试图通过以下简单的方案描述这一点:
假设我们有2个线程A和B.它们都修改了一个纯函数字典D.此时它们不需要锁,因为数据是不可变的,所以它们输出新的字典DA和D B。现在我的问题是如何协调DA和DB,以便以后的操作可以看到数据的单一视图?
编辑:答案建议在DA和DB上使用merge
函数。我不知道如何解决问题,因为可能有另一个与合并操作同时运行的线程C.声称纯粹的功能数据结构是无锁的,但使用合并功能听起来更像是最终的一致性,这是另一回事。
答案 0 :(得分:3)
功能数据结构的要点是原子性。在您的场景中,DA和DB保证一致,如何将它们合并为一致的值取决于您的应用程序(函数式编程不会为您解决所有问题)。然而,这仍然比你在命令式范例中得到的更多,其中DA和DB依赖于线程的排序方式。
答案 1 :(得分:3)
很好的观察。但这仅仅意味着在全球状态下存在并行性。无论编程语言如何,都很难或不可能。
在不纯的语言中,这不太明显。你可能认为你可以摆脱锁定,同步以及围绕它的所有高度复杂的代码。
而在纯语言中,你有n
个纯状态变换器,并且不得不意识到如果你将它们应用于pareallel到某个初始状态S0
,你最终会得到很多状态S1
,S2
,S3
... Sn
现在,纯度只是保证S0
的并行转换不会以任何方式相互干扰。它不能解决您的程序设计问题,例如该状态意味着什么,如果它可能太粗糙,或者太细粒度,并且您可以从各种转换状态计算最终结果。
答案 2 :(得分:2)
一般来说,有很多方法可以做到这一点。实际上,您可能希望确保DA或DB是规范的。如果无法做到那么应该有一个合并过程
Thread A/B:
da <- readSharedMemory at D
da' <- modifyDict da
mergeSharedMemory da'
其中mergeSharedMemory
能够以一致的方式组合具有相似历史的两个词典。通常,这些类型的结构在数据库文献中作为CRDT-“会聚复制数据类型”和“可交换复制数据类型”进行了充分研究。 INRIA Paper有很多细节。
答案 3 :(得分:2)
纯函数数据结构是制作无锁可变数据结构的最简单的方法。给定一个任意的纯函数数据结构 T
,您可以定义一个无锁可变数据结构 newtype U = U (IORef T)
。给定一个任意函数 f :: T -> (T, a)
,你可以写
mf :: U -> IO a
mf (U ref) = atomicModifyIORef' ref f
那么,为什么会有大量关于制作无锁数据结构的奇特技术的文献呢?因为并非所有数据结构都可以以纯函数方式足够有效地实现。任何耗时过长的操作都会减慢所有线程的速度并导致更新积压。
答案 4 :(得分:1)
您创建第三个使用DA和DB生成DAB的函数。