Linda: 比 Actor 更好的并发模型
文章目录
Think and work in the future, not the present or past – Alan Key How?
前言
两个并行的进程可能会相互通信;一种语言的程序可能会使用通信机制与另一种语言的程序进行通信;一个用户程序可能会与操作系统进行通信;一个程序通过写文件的方式与自己的某个未来版本进行通信。大多数系统将这些事情分类在不同的、不相关的机制下,但 Linda 的 tuple 模型以一致的方式处理它们
Alan Kay(这篇访谈) 和 Bret 都偏好 Linda。
如果我们把计算机看作是对象之间(人、虚拟事物、现实事物、环境)彼此交流(talk)的环境(Dynamicland 是这样的环境,CodeLab Neverland 也是),那么 Linda 是更好地做这件事的出发点, 这是我关注 Linda 的原因.
Dynamicland 背后是 Realtalk, Realtalk 可能在 Lua 之上实现了 Linda(目前尚无确切信息),这是一种新的操作系统。
CodeLab Neverland 背后则是 CodeLab Adapter。我们目前计划在 CodeLab Adapter 的下个大版本(4.0)里实现 Linda,目前 CodeLab Adapter 本质上是个消息传递(message-pass)系统,基于 Pub/Sub(受 ROS 影响),我们将同步模型实现在 client 一侧(在异步之上实现的同步模型),异步是比同步典型/常见的模式(论文里提到),也是在这个意义上,我们相信 Adapter 基于 Pub/Sub 的模型,比 Smalltalk-80 的消息传递模型强大。
LINDA IN CONTEXT 讨论了 相比于主流并发模型,Linda 更好的原因。
文章将 Linda 和以下三种风格的并发模型做了对比:
- 基于消息传递的并发面向对象编程
- 并发逻辑编程
- 函数式编程
我目前只关注第一项: 基于消息传递的面向对象并发编程(这是个伪概念,下文会说到)。
复述整篇论文意义不大,本文只摘录和讨论我关心的部分(view point)。
本文可视为LINDA IN CONTEXT这篇论文的阅读笔记,同时加了一些阅读其他材料所做的笔记以及我自己的观点。
Linda 的想法
Linda 采用一种生成式通信(generative communication)风格。
两个进程需要进行通信,它们不会交换消息或共享变量。相反,进程会生成新的数据对象(称为 tuple(元组))(似乎受到 LISP 关于数据不可变观点的影响),并将其放入一个叫做 tuple 空间的区域。接收者进程可以访问 tuple,获取数据。
在 Linda 模型中,通信和进程创建是同一操作的两面。要创建进程,就生成活的tuple(live tuple),计算完成后,将变成普通的、数据化的tuple。
一个新的 tuple 被添加到元组空间(tuple space),任何感兴趣的进程都可以访问它(可以将其移除,也可以只是读取)。其次,数据以持久对象的形式存在,而不是转瞬即逝的消息。
Linda 则提供了四个基本操作:
- eval 和 out 来创建新的数据对象
- in 和 rd 分别删除(读并删除)和读取(只读不删除)它们。
Linda 不插手计算,所以它可以轻易与其他编程模型配合使用。
基于消息传递(message-pass)的并发
当前计算机领域最著名的并行编程技术是消息传递(message-pass)。
这种编程模型包含以下操作:
- create-process
- send-message
- receive-message
基于消息传递(message-pass)的并发模型有什么问题呢?
这些问题包括:
- 消息的瞬时性
- 发送者与接受者的耦合
第 1 个问题,消息是一种转瞬即逝的东西,这可能让许多编程场景变得复杂,为此,可能需要引入额外的很多系统支持,诸如存储之类,这些都可能让并发问题进一步复杂。
第 2 个问题, 在消息传递系统中,一则消息必须 明确 地指向某个接收者。而且只有那个接收器可以读取它(除非程序使用了某种广播(broadcast)操作,有些消息系统提供,有些则不提供)。这导致了耦合,包括时间上的耦合(生命周期依赖)。在 Linda 中,发送方和接受方彼此不知道对方的存在(很像pub/sub风格), 它支持非耦合的编程风格。
面向对象?
有一个时髦的说法叫 基于消息传递的并发面向对象编程,Actor 多少也与此有关。
首先,LINDA IN CONTEXT 有力论述了面向对象与并发是无关的两个概念, 所以 基于消息传递的面向对象并发 这个词汇没什么意义。注意力放在 基于消息传递的并发 上即可。
当我们谈论 基于消息传递的并发面向对象编程 时,多数时候说的是基于监视器(monitor)的多对象并发机制。
基于 monitor 的多对象并发编程
一个对象定义了可以被其他对象调用的方法。方法调用本质上是一种同步操作:过程调用会阻塞,直到返回一个结果。
由于可能被多个对象并发调用,于是通过监控器(monitor)来确保资源每次只被一个对象操作(锁住它)。
但是,并行程序中的进程通常不会关心他们的数据会发生什么,使用像 Linda 的 out 这样的异步操作比同步过程调用更有效率,在概念上也更贴切。使用 out,一个进程可以将一个结果转储到元组空间,然后去做其他事。(与 Pub 相似)
可以将面向对象的语言与 Linda 结合起来。使用 out(对于被动对象)或 eval(主动对象)生成对象。所有与主动对象的通信都经过 tuple 空间。同样,对象本身并不在并行模型中。(正交)
Actors 的问题
在 基于消息传递的并发模型中, Actor 尤为著名。
在 Actors 系统中,进程(称为 Actors)通过互相发送消息进行通信(称为 “tasks”)。一个进程可以通过生成一个或多个新进程来响应消息。这种系统很容易在 Linda 中表达出来。
Actors 基本上是一个消息传递模型。 所以和其他基于消息模型的并发模型一样,受到消息传递模式本身的限制。
Actors 模型似乎比 Linda 涵盖的编程模式更少,但方式更复杂。
在 Actors 模型中,一个进程和一条消息是独立的结构;在 Linda 中,一个进程变成了一个元组(一体两面)
Actors 消息包含三个独立的部分:
- 标签
- 目标
- 通信(communication)
元组只是一组字段,可以轻松包含:
- 标签(即消息 id)
- 目标(地址)
- 通信(数据)。
Actors 不支持关联寻址,也不允许将消息发送给尚未创建的进程。在 Linda 中,一个元组可以被未来才创建的进程所操作(持久性、时间跨越)。
Actors 模型是由一个相当复杂的形式化框架支持的,Linda 则很简单。
Dynamicland
Dynamicland 背后的系统 – Realtalk, 似乎是在 Lua 之上实现了 Linda。
目前社区中有一些 Lua 中的 Linda 实现,如Lua Lanes。目前尚不清楚 Dynamicland 的具体做法。
我的计划
为 CodeLab Adapter 下个版本引入 Linda 模型,使得协作可以更容易发生,包括以下几种协作:
- 程序之间(程序对象被看作是递归的计算机)的协作
- 在可计算环境(房间就是计算机)中,人与人、人与程序之间的协作,程序是人的代理(agency),所以最终还是还原为 程序之间的协作。
CodeLab Adapter 的一个使用场景是用于构建 Neverland (一种受到 Dynamicland 启发的计算环境,将整个空间视为一个计算机)
Python 实现
由于 CodeLab Adapter 目前是在 Python 实现的,所以我需要一个 Linda 模型的 Python 实现,目前社区里的实现都不理想
其中 Linda in Python 尤为简单,可作为不错的脚手架。
站在 Smalltalk 角度
Cosmic metaphors really help imagination – Alan Key How?
我目前这样思考 Linda: Linda是一种信息/数据(不使用消息,因其转瞬即逝)存在的媒介(环境),进程/对象 之间经过这个媒介交流。 它和smalltalk的对象/消息隐喻(对象通过流动的消息交流)是不同的, 在smalltalk 里,对象之间是直接交流。在流动的消息这个观点上,Alan Kay会说:
Synergy requires constant messaging(协同需要持续的消息传递)
但Linda似乎并不接受这种见解。
我对 Smalltalk 的理解是: 小物体之间彼此交互(small object , talk with each other)。 小物体(程序/对象)被看作计算机的递归,他们独立处理自身接收到的消息,并做出反应,听起来像一个小生物体(Alan Kay 有生物学背景)。
Smalltalk环境里物体之间彼此独立,不耦合,系统拥有极高的灵活性和表达能力,每个 object 都根据从其他对象那里得来的消息(沿着这个隐喻,我们可以说消息流动的通道(诸如网络)充当了类似物理环境信息介质(空气、水)的角色)来决策。这不像计算机环境,像生物/物理环境。
但目前 smalltalk 对于 object 之间如何交流的模型,却不如人意,它本质上是同步的,而且由于是消息系统,导致了耦合(前文有论述)。smalltalk 对象之间沟通的模型(send message)可以通过阅读smalltalk bluebook获知,更好的方式是基于异步实现。
我想这是 Alan Kay 偏好 Linda 的原因,能够更好实现 晚绑定(late binding) , 值得一提的是,晚绑定是 Alan Kay 设计系统时追求的特性,至于面向对象和消息,只是实现这个特性的策略,他们不是目的本身。
我觉得 Linda 似乎受到 Smalltalk 传统的深远影响, 将**晚绑定(late binding)**的目标进一步推进。
这是一种更好的 **协作** 模型, 并发只是技术和时间层面的视角,相对于协作的概念,它也不太重要我的实现
我目前在 Python 中,正基于 pub/sub 和 协程实现 Linda。
参考
- LINDA IN CONTEXT
- wikipedia Linda
- wikipedia Actor
- Squeak Actors
- Relationship between OO and functional programming?
- 通过描述匹配,以一般的发布(out)和描述(in)的方式做松散耦合。我还是很喜欢这个想法,并希望看到它能推进到对象之间真正可以 “协商意义 “的地步。
- 普通的逻辑中,不包含时间。John McCarthy 通过给所有的 “事实” 添加一个额外的参数来解决这个问题,这个参数代表了一个事实为真时的 “时间框架”。这创造了一个简单的时间逻辑,将 “事实集合 “可视化为世界线(world-line)的堆叠 “层”。
- “旧 “值(来自于前一帧)
- 同样值得关注的是数据库上的 “原子事务”。以一种非破坏性的方式创建的,没有竞赛条件。有一个版本的历史。
- 刚刚计算出来的稳定状态是非常有用的。它永远不会再被改变–所以它代表了系统模拟的一个 “版本”
- “稳定状态之间不存在时间”:“时钟 “只有在每个新状态完成时才会前进。就程序而言,CPU本身并不充当时钟。它有一个内在的、干净的时间模型
- 在命令式编程中允许竞争条件,然后试图使用可怕的信号符等来保护它们,这可能导致锁定。
- 任何对对象之间的消息传递感兴趣的人,如果知道Lisp,就会被吸引到 “apply”,并注意到一种对象(一个lambda “东西”,它可能是一个闭包)被绑定到参数上(这有点像消息)。未评估的表达式可以作为参数传递,并在之后进行求值。这使得那些不雅的 “特殊形式”(如条件)可以被省去,它们可以被写成一种普通的懒惰函数。
- 通过上面提到的时空建模,可以松开 “eval-apply “的 “齿轮”,通过安全消息传递得到时空层之间的功能关系。
- 因为我一直喜欢用 “模拟的视角 “来看待计算,所以我认为 “对象 “和 “函数 “是相辅相成的思想,完全不冲突。
- 对象就可以完成处理设计参数(包括保留历史记录等)所需的操作。
文章作者 种瓜
上次更新 2020-11-27