源于从Erlang到Go的一些思维碰撞,就像当初从C++到Erlang一样,整理下来记于此。

Actor

Actor模型,又叫参与者模型,其”一切皆参与者(actor)”的理念与面向对象编程的“一切皆是对象”类似,但是面向对象编程中对象的交互通常是顺序执行的(占用的是调用方的时间片,是否并发由调用方决定),而Actor模型中actor的交互是并行执行的(不占用调用方的时间片,是否并发由自己决定)。

在Actor模型中,actor执行体是第一类对象,每个actor都有自己的ID(类比人的身份证),可以被传递。actor的交互通过发送消息来完成,每个actor都有一个通信信箱(mailbox,本质上是FIFO消息队列),用于保存已经收到但尚未被处理的消息。actorA要向actorB发消息,只需持有actorB ID,发送的消息将被立即Push到actorB的消息信箱尾部,然后返回。因此Actor的通信原语是异步的。

从actor自身来说,它的行为模式可简化为:

  • 发送消息给其它的actor
  • 接收并处理消息,更新自己的状态
  • 创建其它的actor

一个好的Actor模型实现的设计目标:

  • 调度器: 实现actor的公平调度
  • 容错性: 具备良好的容错性和完善错误处理机制
  • 扩展性: 屏蔽actor通信细节,统一本地actor和远程actor的通信方式,进而提供分布式支持
  • 热更新? (还没弄清楚热更新和Actor模型,函数式范式的关联性)

在Actor模型上,Erlang已经耕耘三十余载,以上提到的各个方面都有非常出色的表现,其OTP整合了在Actor模型上的最佳实践,是Actor模型的标杆。

CSP

顺序通信进程(Communicating sequential processes,CSP)和Actor模型一样,都由独立的,并发的执行实体(process)构成,执行实体间通过消息进行通信。但CSP模型并不关注实体本身,而关注发送消息使用的通道(channel),在CSP中,channel是第一类对象,process只管向channel写入或读取消息,并不知道也不关心channel的另一端是谁在处理。channel和process是解耦的,可以单独创建和读写,一个process可以读写(订阅)个channel,同样一个channel也可被多个process读写(订阅)。

对每个process来说:

  • 从命名channel取出并处理消息
  • 向命名channel写入消息
  • 创建新的process

Go语言并没有完全实现CSP理论(参见知乎讨论),只提取了CSP的process和channel的概念为并发提供理论支持。目前Go已经是CSP的代表性语言。

CSP vs Actor

  • 相同的宗旨:”不要通过共享内存来通信,而应该通过通信来共享内存”
  • 两者都有独立的,并发执行的通信实体
  • Actor第一类对象为执行实体(actor),CSP第一类对象为通信介质(channel)
  • Actor中实体和通信介质是紧耦合的,一个Actor持有一个Mailbox,而CSP中process和channel是解耦的,没有从属关系。从这一层来说,CSP更加灵活
  • Actor模型中actor是主体,mailbox是匿名的,CSP模型中channel是主体,process是匿名的。从这一层来说,由于Actor不关心通信介质,底层通信对应用层是透明的。因此在分布式和容错方面更有优势

Go vs Erlang

  • 以上 CSP vs Actor
  • 均实现了语言级的coroutine,在阻塞时能自动让出调度资源,在可执行时重新接受调度
  • go的channel是有容量限制的,因此只能一定程度地异步(本质上仍然是同步的),erlang的mailbox是无限制的(也带来了消息队列膨胀的风险),并且erlang并不保证消息是否能到达和被正确处理(但保证消息顺序),是纯粹的异步语义,actor之间做到完全解耦,奠定其在分布式和容错方面的基础
  • erlang/otp在actor上扩展了分布式(支持异质节点),热更和高容错,go在这些方面还有一段路要走(受限于channel,想要在语言级别支持分布式是比较困难的)
  • go在消息流控上要做得更好,因为channel的两个特性: 有容量限制并独立于goroutine存在。前者可以控制消息流量并反馈消息处理进度,后者让goroutine本身有更高的处理灵活性。典型的应用场景是扇入扇出,Boss-Worker等。相比go,erlang进程总是被动低处理消息,如果要做流控,需要自己做消息进度反馈和队列控制,灵活性要差很多。另外一个例子就是erlang的receive操作需要遍历消息队列(参考),而如果用go做同步调用,通过单独的channel来做则更优雅高效

Actor in Go

在用Go写GS框架时,不自觉地会将goroutine封装为actor来使用:

  • GS的执行实体(如玩家,公会)的逻辑具备强状态和功能聚合性,不易拆分,因此通常是一个实体一个goroutine
  • 实体接收的逻辑消息具备弱优先级,高顺序性的特点,因此通常实体只会暴露一个Channel与其它实体交互(结合go的interface{}很容易统一channel类型),这个channel称为RPC channel,它就像这个goroutine的ID,几乎所有逻辑goroutine之间通过它进行交互
  • 除此之外,实体还有一些特殊的channel,如定时器,外部命令等。实体goroutine对这些channel执行select操作,读出消息进行处理
  • 加上goroutine的状态数据之后,此时的goroutine的行为与actor相似:接收消息(多个消息源),处理消息,更新状态数据,向其它goroutine发送消息(通过RPC channel)

到目前为止,goroutine和channel解耦的优势并未体现出来,我认为主要的原因仍然是GS执行实体的强状态性和对异步交互流程的顺序性导致的。

在研究这个问题的过程中,发现已经有人已经用go实现了Actor模型: https://github.com/AsynkronIT/protoactor-go。 支持分布式,甚至supervisor,整体思想和用法和erlang非常像,真是有种他山逢知音的感觉。:)

参考:

  1. http://jolestar.com/parallel-programming-model-thread-goroutine-actor/
  2. https://www.zhihu.com/question/26192499

http://wudaijun.com/2017/05/go-vs-erlang/

CSP vs Actor Go vs Erlang的更多相关文章

  1. 关于并发模型 Actor 和 CSP

    最近在看<七天七并发模型>这本书,在书上介绍了 Actor 和 CSP 这两种并发模型.这两种模型很像,但还是有一些不同的地方.看完之后,比较困扰的是: 在什么场合使用哪种模型比较好呢? ...

  2. Go/Python/Erlang编程语言对比分析及示例

    本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性,不过最主要的原因是这几个我比较熟悉. Go的很多语言特性借鉴 ...

  3. 并发之痛 Thread,Goroutine,Actor

    转自:http://jolestar.com/parallel-programming-model-thread-goroutine-actor/ 先梳理下两个概念,几乎所有讲并发的文章都要先讲这两个 ...

  4. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  5. 基于 CSP 的设计思想和 OOP 设计思想的异同

    LinkerLin Go语言推崇的CSP编程模型和设计思想,并没有引起很多Go开发者包括Go标准库作者的重视.标准库的很多设计保留了很浓的OOP的味道.本篇Blog想比较下从设计的角度看,CSP和OO ...

  6. 基于Actor模型的CQRS、ES解决方案分享

    开场白 大家晚上好,我是郑承良,跟大家分享的话题是<基于Actor模型的CQRS/ES解决方案分享>,最近一段时间我一直是这个话题的学习者.追随者,这个话题目前生产环境落地的资料少一些,分 ...

  7. Actor模型的状态(State)+行为(Behavior)+邮箱(Mailbox)

    状态(State)+行为(Behavior)+邮箱(Mailbox) 基于Actor模型的CQRS.ES解决方案分享 开场白 大家晚上好,我是郑承良,跟大家分享的话题是<基于Actor模型的CQ ...

  8. Golang全接触

    满打满算, 从好友推荐Golang至发文时, 使用Golang已经有1年多了. 这种时间对于C/C++ Java这些老者来说, 简直是菜鸟级别的经验 但作为新生代语言的特点就是实战. Golang这一 ...

  9. Python协程与Go协程的区别二

    写在前面 世界是复杂的,每一种思想都是为了解决某些现实问题而简化成的模型,想解决就得先面对,面对就需要选择角度,角度决定了模型的质量, 喜欢此UP主汤质看本质的哲学科普,其中简洁又不失细节的介绍了人类 ...

随机推荐

  1. MVC登录前准备写好cookie

    Insus.NET写过一系列的MVC的练习,昨天学习了jQuery的验证<在MVC应用程序中使用jQuery的验证>http://www.cnblogs.com/insus/p/34626 ...

  2. Android-Menu菜单使用一

    创建菜单 在AndroidSDK中,无需从头创建菜单对象.因为一个活动只与一个菜单关联,所以Android会为该活动创建此菜单,然后将它传给Activity类的onCreateOptionsMenu回 ...

  3. 你不知道的JavasScript上篇·第四章·混合对象·类

    一.类的理论 1.类的核心概念:多态 是说父类的通用行为可以被子类用更特殊的行为重写 二.类的机制 1.构造函数 类实例是有一个特殊的类方法构造的,这个方法名通常和类名一致: 类构造函数属于类,构造函 ...

  4. PHP 类文件的自动加载机制 __autoload()

    如果一个类在多个脚本中都需要使用,可以将一个类的定义代码,单独的封装到一个文件中,这种文件也叫作类文件,在需要的时候,将整个文件载入进来即可! PHP在执行的时候,如果发现需要一个类(只要是和这个类相 ...

  5. 排序算法(7)--exchang Sorting--交换排序[2]--Quick Sort--快速排序

    1.基本思想 从待排序列中选取一元素作为轴值(也叫主元). 将序列中的剩余元素以该轴值为基准,分为左右两部分.左部分元素不大于轴值,右部分元素不小于轴值.轴值最终位于两部分的分割处. 对左右两部分重复 ...

  6. BZOJ4358: permu(带撤销并查集 不删除莫队)

    题意 题目链接 Sol 感觉自己已经老的爬不动了.. 想了一会儿,大概用个不删除莫队+带撤销并查集就能搞了吧,\(n \sqrt{n} logn\)应该卡的过去 不过不删除莫队咋写来着?....跑去学 ...

  7. JS中文档碎片的理解和使用

    1.我们要明白当js操作dom时发生了什么? 每次对dom的操作都会触发"重排"(重新渲染界面,发生重绘或回流),这严重影响到能耗,一般通常采取的做法是尽可能的减少 dom操作来减 ...

  8. es6 export与export default 的区别

    相同点: 均可用于导出常量.函数.文件.模块等 不同点: 1.在一个文件中export可以有多个,但export default 只能有一个: export var firstName = 'Mich ...

  9. jQuery同步Ajax带来的UI线程阻塞问题

    一.需求 在调ajax接口的时候因为有时间延迟,想要做一个loading加载的效果,等数据返回时再把loading的效果去掉. 所以我在调ajax的代码块前面加了显示loading效果的代码,ajax ...

  10. 2018-10-23 23:29:54 clanguage

    2018-10-23   23:29:54 clanguage 在 32 位环境以及 Win64 环境下的运行结果为: short=2, int=4, long=4, char=1 在 64 位 Li ...