小小千想和您聊一聊

当前位置: 首页> 技术分享> Golang 的并发与 Erlang、Scala、Node.js 和 Python 的并发模型相比有何特点?

Golang 的并发与 Erlang、Scala、Node.js 和 Python 的并发模型相比有何特点?

  首先,说一下Golang的并发:

  Go的很多语言特性借鉴与它的三个祖先:C,Pascal和CSP。Go的语法、数据类型、控制流等继承于C,Go的包、面对对象等思想来源于Pascal分支,而Go最大的语言特色,基于管道通信的协程并发模型,则借鉴于CSP分支。

  Golang 就是借用CSP模型的一些概念为之实现并发进行理论支持,其实从实际上出发,go语言并没有,完全实现了CSP模型的所有理论,仅仅是借用了 process和channel这两个概念。process是在go语言上的表现就是 goroutine 是实际并发执行的实体,每个实体之间是通过channel通讯来实现数据共享。

  Channel

  Golang中使用 CSP中 channel 这个概念。channel 是被单独创建并且可以在进程之间传递,它的通信模式类似于 boss-worker 模式的,一个实体通过将消息发送到channel 中,然后又监听这个 channel 的实体处理,两个实体之间是匿名的,这个就实现实体中间的解耦,其中 channel 是同步的一个消息被发送到 channel 中,最终是一定要被另外的实体消费掉的,在实现原理上其实是一个阻塞的消息队列。

  Goroutine

  Goroutine 是实际并发执行的实体,它底层是使用协程(coroutine)实现并发,coroutine是一种运行在用户态的用户线程,类似于 greenthread,go底层选择使用coroutine的出发点是因为,它具有以下特点:

  l 用户空间 避免了内核态和用户态的切换导致的成本

  l 可以由语言和框架层进行调度

  l 更小的栈空间允许创建大量的实例

  可以看到第二条 用户空间线程的调度不是由操作系统来完成的,像在java 1.3中使用的greenthread的是由JVM统一调度的(后java已经改为内核线程),还有在ruby中的fiber(半协程) 是需要在重新中自己进行调度的,而goroutine是在golang层面提供了调度器,并且对网络IO库进行了封装,屏蔽了复杂的细节,对外提供统一的语法关键字支持,简化了并发程序编写的成本。

  Go语言也能够实现传统的共享内存的通信方式,但Go更提倡“以通信来共享内存,而非以共享内存来通信”。Go的并发通信方式借鉴CSP(Communicating Sequential Process)模型,其主要特征如下:

  1、goroutine(协程,Go的轻量级线程)是Go的轻量级线程管理机制,用“go”启动一个goroutine, 如果当前线程阻塞则分配一个空闲线程,如果没有空闲线程,则新建一个线程;

  2、通过管道(channel)来存放消息,channel在goroutine之间传递消息;比如通过读取channel里的消息(通俗点说好比一个个“值”),你能够明白某个goroutine里的任务完成以否;

  3、Go给channel做了增强,可带缓存。

  第二,Scala

  Scala语言并发设计采用Actor模型,借鉴了Erlang的Actor实现,并且在Scala 2.10之后,Scala采用的是Akka Actor模型库。Actor模型主要特征如下:

  1、“一切皆是参与者”,且各个actor间是独立的;

  2、发送者与已发送消息间解耦,这是Actor模型显著特点,据此实现异步通信;

  3、actor是封装状态和行为的对象,通过消息交换进行相互通信,交换的消息存放在接收方的邮箱中;

  4、actor可以有父子关系,父actor可以监管子actor,子actor唯一的监管者就是父actor;

  5、一个actor就是一个容器,它包含了状态、行为、一个邮箱(邮箱用来接受消息)、子actor和一个监管策略;

  Scala与Go在并发通信模型实现上的主要差异如下:

  1、actor是异步的,因为发送者与已发送消息间实现了解耦;而channel则是某种意义上的同步,比如channel的读写是有关系的,期间会依赖对方来决定是否阻塞自己;

  2、actor是一个容器,使用actorOf来创建Actor实例时,也就意味着需指定具体Actor实例,即指定哪个actor在执行任务,该actor必然要有“身份”标识,否则怎么指定呢?!而channel通常是匿名的,任务放进channel之后你不用关心是哪个channel在执行任务

  第三:erlang

  Go和Erlang的并发模型都来源于CSP,但是Erlang是基于actor和消息传递(mailbox)的并发实现,Go是基于goroutine和管道(channel)的并发实现。不管Erlang的actor还是Go的goroutine,都满足协程的特点:由编程语言实现和调度,切换在用户态完成,创建销毁开销很小。

  在erlang,实现并发通过语言级别的三个并发原语实现。另外,erlang是面向消息的语言,即,进程间通信不是通过共享内存等方式实现,而是通过消息实现。Erlang中也不存在线程的概念,通过产生多个进程来实现并发。

  注:erlang中创建一个进程的开销远远小于在其他语言中创建一个进程。因为erlang创建进程的过程是在erlang自身的虚拟机中实现,此处的底层实现保证了创建进程的快速以及低开销。

  第四:Nodejs

  nodejs是单线程且支持高并发的脚本语言。

  第五:Python

  至于Python,其多线程的切换和调度是基于操作系统实现,而且因为GIL的大坑级存在,无法真正做到并行。

上一篇:HTML5工具初识之网页编辑器

下一篇:linux操作系统内配置vlan+kvm虚拟机

QQ技术交流群

千锋go语言官方①群
816085262

加入群聊