我最近读了很多关于函数式语言的文章。由于那些只使用不可变结构,因此他们声称并发问题得到了极大改善/解决。我在理解如何在真实环境中实现这一点时遇到了一些麻烦。让我们假设我们有一个网络服务器,其中一个线程正在监听端口(好吧,IO是另一件我难以绕过头脑的东西,但现在让我们忽略它);在任何连接尝试中,创建套接字并将其传递给新创建的线程,该线程对其进行一些操作,并且根据接收的通信,可以将更改应用于对服务器应用程序是全局的大列表/数据结构。那么,这个列表访问如何工作,以便所有线程具有一致的列表视图(或者至少为了一旦线程以正确的方式死亡,将一个线程所做的所有更改应用于列表) ?
我的理解问题是:
那么你如何用函数式语言模拟这样的东西呢?
答案 0 :(得分:19)
不可变值有几个适合它们的应用程序。并发/并行处理只是其中之一,最近变得更加重要。以下是经验中最基本的摘要以及关于该主题的许多书籍和谈话。您最终可能需要深入了解一些。
您在这里展示的主要示例是关于管理全局状态,因此不能完全“不可变”地完成。但是,即使在这里,也有很好的理由使用不可变数据结构。其中一些来自我的头脑:
回到你的问题。
在最简单的情况下,在这种情况下,全局状态通常使用顶部的一个可变引用建模到不可变数据结构。
仅通过CAS原子操作更新引用。
不可变数据结构由无副作用的函数转换,当完成所有转换时,引用将以原子方式交换。
如果两个线程/核心想要同时交换从同一个旧值获得的新值,那么首先获胜的另一个不会成功(CAS语义)并且需要重复操作(取决于转换,要么更新)具有新值的当前值,或从头开始转换新值)。这可能看起来很浪费,但这里假设重做一些工作通常比永久锁定/同步开销更便宜。
当然这可以优化,例如通过划分不可变数据结构的独立部分,通过独立更新多个引用来进一步减少潜在的冲突。
对数据结构的访问是无锁且非常快速的,并始终提供一致的响应。边缘情况,例如当您发送更新而另一个客户端之后接收到旧数据时,在任何系统中都会出现这种情况,因为网络请求也会出现故障......
STM非常有用,通常您最好使用包含来自STM事务中使用的引用的所有值的数据结构的原子交换。