笔记:《七周七并发模型》

书的七部分中,前五部分介绍的都是语言级别的并行与并发,两部分介绍了特殊设备(GPU)和计算机集群两个角度下的并行计算。

并行与并发引发的问题,根据我现在的理解,分为竞争与通信。

竞争

竞争在java章节中需要使用锁进行控制。

而在后续四章中:

  • 函数式设计避免修改数据,消除了竞争;
  • 在需要可变量时,提供了原子变量、agent、STM;
  • Elixir中的actor并不能直接修改其它actor的数据,actor间通信是由系统信箱维护的;
  • CSP机制与actor异曲同工,线程间通信由第三者(channel)管理。

可以看到后四者提供的编程界面都是线程安全的,而这些编程界面保证用户代码不产生竞争。

通信

在java章节中,通信问题通过阻塞队列解决,并且还可以学到:对于并行算法,共有数据会成为瓶颈,于是应该尽量在线程本地处理数据,再提供合并机制。当站在集群的角度看单个计算机,相似观点催生了最后一部分的MapReduce。

函数式设计中的并行通信同样是通过阻塞实现的(future和promise)。

介绍clojure可变量的章节为数据访问冲突提供了两种具体方法:

  1. 建立在持久化数据上的重试机制,原子变量和STM。
  2. 异步化数据写入,由系统控制真正的写入过程,agent。

actor模型将通信问题真正视为通信,在单一actor(线程)的视角,通信就是接收消息和发出消息,消息的传递由系统负责。

CSP与actor类似,在线程间设立通信对象channel,但是CSP更关注channel上的操作。go块借助channel实现了非连续运行的逻辑流程。

总结来看:

  • agent和actor和CSP都通过系统将并行的访问转化为串行访问,于是在使用者看来,世界是串行的。

  • 原子变量、STM、agent是全局共享数据的安全机制。透过其中存储的数据也可以实现进度控制。而

  • actor和CSP更关注运行过程,并不设全局变量,用消息传递一切。

控制反转

这本书中最使我眼前一亮的莫过于控制反转的go机制,它使我想到了haskell中的do机制。

go机制与monad其实非常相似,书中go机制所要处理的是回调困境:需要将一个完整的事务逻辑拆散并嵌入到事务的实现细节当中去。在网页中,一个多阶段的事务(比如,分散在多个显示页面中的注册信息),运行过程是由用户主动发起的(点击按钮进入下一步),在js中的典型解决方案就是回调,以点击事件为界将完整的逻辑分散到若干个回调函数中去,这使得实现不能直观表征逻辑,即增加了复杂性。

go机制依托降低线程成本的底层实现,并以channel抽出了现实流程:通过channel表示鼠标点击事件,于是就可以在一个逻辑单位(go块)中描述业务逻辑。

如果需要在haskell中处理相似问题,思路也是相似的。不同之处在与go的底层通过线程调整实现了分阶段运行(状态机化),而在haskell中,do机制可以被变换为分散的代码块(可以理解为完整逻辑被haskell拆散并嵌入到实际运行过程中)。