基于消息传递的并发模型
An object oriented language is a language with good support for objects. A concurrency oriented language has good support for concurrency. --Joe Armstrong
两类通用并发模型:参考七周七并发模型
- 共享内存型Shared Memory
- 线程Threads
- 锁Locks
- 互斥l量Mutexes
- 消息传送型(CSP和Actor模型)
- 进程Processes
- 消息Messages
- 不共享数据(状态)No shared data
重点介绍消息传送型的两种模型Actor和CSP(Communicating Sequential Process)的各项对比
主要目的:除了常用的Python、Java等用的并发模型之外,还存在这么个东西
先看两段代码
代码示例对比
使用Erlang代码和Go代码分别实现打印服务print_server,用来对比模型使用差异
Actor模型-Erlang代码
%%%-------------------------------------------------------------------
%%% @author Suncle
%%% @doc
%%% print_server
%%% @end
%%% Created : 2017/12/18 14:53
%%%-------------------------------------------------------------------
-module(print_server).
-author("Flowsnow").
%% API
-export([print_server/0, start_print_server/0, send_msg/2]).
print_server() ->
receive
Msg ->
io:format("print_server received msg: ~p~n", [Msg]),
print_server()
end.
start_print_server() ->
Pid = spawn(?MODULE, print_server, []),
Pid.
send_msg(Msg, Pid) ->
Pid ! Msg,
io:format("send_normal_msg: ~p~n", [Msg]).
Erlang shell输出结果如下:
1> c("print_server.erl").
{ok,print_server}
2> Pid = print_server:start_print_server().
<0.39.0>
3> print_server:send_msg("hello", Pid).
send_normal_msg: "hello"
print_server received msg: "hello"
ok
以上print_server使用的是最原始的Erlang语法实现的,也可以使用OTP gen_server原语实现更加清晰易懂
CSP模型-Go代码
print函数从channel读取消息并阻塞,直到主函数向channel写入hello消息
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan string)
go print(c)
time.Sleep(1 * time.Second)
fmt.Println("main function: start writing msg")
c <- "hello"
var input string
fmt.Scanln(&input)
}
func print(c <-chan string) {
fmt.Println("print function: start reading")
fmt.Println("print function: reading: " + <-c)
time.Sleep(1 * time.Second)
}
输出结果如下:
D:workspaceGo>go run print_server.go
print function: start reading
main function: start writing msg
print function: reading: hello
模型图对比
Actor
Actor1发送消息到Actor2的邮箱中,邮箱本质是队列,由Actor2消费
CSP
Process1在Channel的写入端添加消息,Process2在channel的读取端读取消息
基本特性对比
Actor
- 基于消息传递message-passing
- 消息和信箱机制:消息异步发送
- 保留可变状态但不共享
- 失败检测和任其崩溃
- 重点在于发送消息时的实体
CSP
- 基于消息传递message-passing
- 顺序进程Sequential processes
- 通过channel同步通信Synchronous communication through channels
- 频道交替复用Multiplexing of channels with alternation
- 重点在于发送消息时使用的通道channel
通信语义对比
Actor
Actor1等待消息并阻塞,直到Actor2发送消息给Actor1 Actor2发送消息给Actor3,暂存在Actor3的Mailbox中,直到Actor3接受并处理
CSP
Process1读取channel因没有消息阻塞,直到Process2向该channel添加消息 process2向channel添加消息并阻塞,直到Process3读取该channel消息
Erlang实现简易银行账户
使用Erlang原语,代码如下:
使用OTP的gen_server,代码如下:
Erlang小项目:IP数据库
使用Erlang/OTP实现的IP数据库,可以根据IP查询到具体的国家省份等,代码如下:
不一样的Erlang特性
- Let it crash思想:值得借鉴
比如:执行算术异常崩溃
- 变量是不可变的,变量一旦赋予值就无法再改变:带来的好处就是没有可变状态,就不需要内存共享,也就不需要有锁
- Erlang进程之间的唯一交互方式就是消息传递:Erlang中没有像C++那样,进程间拥有多种不同的交互方式(管道、消息队列、存储共享等等)。
FAQ
为什么没有容量自动增大的缓冲区?
即使现在有一个看上去永不枯竭的资源,总有一天这个资源还是会被用尽的。可能是因为时过境迁,当初的老程序现在需要解决更大规模的问题;也可能是存在一个bug,消息没有被及时处理,导致被堆积。如果没有思考缓冲区塞满时的对策,那么在未来的某个时间就有可能出现一个破坏性极强,隐蔽性极深且难以诊断的bug。最好的策略是在现在就思考如何处理缓存区被塞满的情况,将问题消灭在萌芽阶段。 因此常用的缓存区类型有三种:阻塞型(blocking),弃用新值型(dropping),移出旧值型(sliding)
Python有什么消息传递并发模型?
Actor模型pykka:https://github.com/jodal/pykka CSP模型pycsp:https://github.com/runefriborg/pycsp/wiki/Getting_Started_With_PyCSP
图片均来源于here!
参考:
- Communicating Sequential Processes (CSP)-An alternative to the actor model
- Concurrency Oriented Programming In Erlang-Joe Armstrong.pdf
- Android SlidingMenu 侧拉菜单的使用(详细配置)
- Rafy 框架 - 幽灵插件(假删除)
- 用粒子群优化算法求解旅行商问题
- 使用CNN(LSTM架构)进行序列预测基于TensorFlow
- 【独家】周志华教授gcForest(多粒度级联森林)算法预测股指期货涨跌
- 如何利用SOTER,1个版本内完成指纹支付开发?
- Rafy 框架 - 大批量导入实体
- Rafy 框架 - 执行SQL或存储过程
- 关于activitygroup过时,用frament替换操作
- Rafy 框架 - 为数据库生成注释
- CNN预测股票走势基于Tensorflow(思路+程序)
- 路径查找器AI
- android推荐使用dialogFrament而不是alertDialog
- 自定义圆角和园边的实现
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 数据库连接池的原理没你想得这么复杂
- 你真的会用volatile吗
- 你真的了解LinkedBlockingQueue的put,add和offer的区别吗
- 关于Java使用groupingBy分组数据乱序问题
- 详解 java CompletableFuture
- 从一个问题来解释下什么是mysql的可重复读
- 每个人都用得到的频数分布直方图
- 利用箱线图巧剔异常值
- dubbo服务接口设计的几个建议
- 使用kafka连接器迁移mysql数据到ElasticSearch
- 这可能是讲雪花算法最全的文章
- 带你了解控制线程执行顺序的几种方法
- 一文说透访问者模式
- 从一个生产上的错误看kafka的消费再均衡问题
- map和object相互转换的几种方法和对比