30

Scala和Golang并发实现对比

 4 years ago
source link: https://www.tuicool.com/articles/JVnqqiR
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

系统中有多个任务同时存在称之为“并发”,并发设计已然成为大规模集群框架的必要特征,本文简单的介绍Scala和golang的并发模型的设计,重点在于比较Scala和Golang在并发实现上的差异。

一、Scala和Golang的并发实现原理

Scala语言并发设计采用Actor模型,采用的是Akka Actor模型库,主要特征如下:

  • “一切皆是参与者”,且各个actor间是独立的;
  • 发送者与已发送消息间解耦,这是Actor模型显著特点,据此实现异步通信;
  • actor是封装状态和行为的对象,通过消息交换进行相互通信,交换的消息存放在接收方的邮箱中;
  • actor可以有父子关系,父actor可以监管子actor,子actor唯一的监管者就是父actor;
  • 一个actor就是一个容器,它包含了状态、行为、一个邮箱(邮箱用来接受消息)、子actor和一个监管策略;
    Go语言与其他语言不同的是,它从语言层面上就支持了并发,Go更提倡“以通信来共享内存,而非以共享内存来通信”,主要特征如下:
  • goroutine(协程,Go的轻量级线程)是Go的轻量级线程管理机制;
  • 通过管道(channel)来存放消息,channel在goroutine之间传递消息,可以缓存;

二、Scala和Golang在并发实现模型上的主要差异:

  • actor是异步的,因为发送者与已发送消息间实现了解耦;而channel则是某种意义上的同步,比如channel的读写是有关系的,期间会依赖对方来决定是否阻塞自己;
  • actor是一个容器,使用actorOf来创建Actor实例时,也就意味着需指定具体Actor实例,即指定哪个actor在执行任务,该actor必然要有“身份”标识,否则怎么指定呢?!而channel通常是匿名的, 任务放进channel之后你不用关心是哪个channel在执行任务;

三、具体实现

说明:对1-10000的整数进行累加,把1-10000平均划分为四部分,启动四个线程进行并发计算,之后将四个线程的运行结果相加得最终得累加值。

Scala实现

Scala定义两个Actor实例,MasterActor和SumActor,前者是后者的父actor,父actor可以监管子actor,子actor唯一的监管者就是父actor。

val sumActor = context.actorOf(Props[SumActor]
                  .withRouter(RoundRobinPool(nrOfInstances = 4)), name = "sumActor")

主程序向MasterActor发送计算指令,然后在线程池中初始化子actor,一共有四个SumActor参与并发计算。

启动计算actor,每个计算线程都会调用计算方法,该方法将处理分段的整数累加,并返回分段累加值给父actor MasterActor。

在整个运算过程中,我们很容易理解发送者与已发送消息间的解耦特征,发送者和接受者各种关心自己要处理的任务即可,比如状态和行为处理、发送的时机与内容、接收消息的时机与内容等。我们可以将更多的精力放在设计上,将各个actor的功能及关联关系表述清楚,剩余的代码实现就非常容易。

Golang实现

Go语言的实现并发功能主要依赖goroutine和channel,首先创建一个计数用的channel,随后发起四个并发计算的协程,

每个计算协程调用计算方法进行分段计算,分段计算完成之后,都会往channel中传入一个计数标志,在率先进入的计数标志没有被读取之前,其他协程在计算结束之后,不能往channel中加入计数标志,只能等待,如此往复,直到channel中的所有计数标志全被取出,意味着所有协程全部处理完毕,在所有的分段计算结束后,就可以计算总的累加值

四、对比Scala和Golang

  • 拿Scala和Go的并发效率来对比,应该是没什么意义的,其间要受到各自内部实现、类型系统、内存使用机制、并发模式、并发规模以及硬件支持等等复杂因素的影响。
  • Akka的actor是解耦的、相对独立的,定义好各个actor间如何沟通,剩下的东西就尽管交给它们处理好了,它们自会按既定方式各司其职,而且每个actor“麻雀虽小五脏俱全”,这也是其解耦性做得好的必然基础。Go语言则独辟蹊径,通过goroutine和channel,以更轻量级的协程方式来处理并发,虽说是更轻量,但你仍得花点心思关注下channel的状态,别一不小心阻塞了,特别是channel数量多逻辑复杂的情况。
  • Akka的Actor实现是库的形式,其也能应用于Java语言。Go语言内嵌了协程的并发实现。
  • Akka基于JVM,实现模式是面向对象,天然讲究抽象与封装,虽然可以穿插混合应用函数式风格。而Go语言显然体现了命令式语言的风格,在需要考虑封装性的时候,需要开发者多着墨。
  • Go语言中轻量级的协程可以轻易启动数十上百万个,这对Scala来说当然是有压力的。但相较而言,Go语言的普及及应用程度尚远不及Java生态,我也希望更多的应用能够实践Go语言。此外,从代码简洁程度来看,Go语言应该会更简洁些吧。

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK