Scala与Golang的并发实现对比
作者:2gua
链接:https://zhuanlan.zhihu.com/p/20009659
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一. Scala与Golang的并发实现思路
Scala语言并发设计采用Actor模型,借鉴了Erlang的Actor实现,并且在Scala 2.10之后,Scala采用的是Akka Actor模型库。Actor模型主要特征如下:
我们来看一个例子:对一组连续序列(1-10000)的整数值进行累加,分别观察Scala与Go环境下单线程与多线程效率,一方面了解并发效率的提升;一方面也能够对比Scala与Go并发实现的差异 ── 这才是本文的重点。具体要求如下:
对1 - 10000的整数进行累加,在并发条件下,我们将1 - 10000平均划分为四部分,启动四个线程进行并发计算,之后将四个线程的运行结果相加得出最终的累加统计值。为了更明显地观察到时间上的差异性,在每部分的每次计算过程中,我们添加一个3000000次的空循环:)
三. Scala实现
以下先列出Scala Akka Actor并发实现的完整示例代码:
链接:https://zhuanlan.zhihu.com/p/20009659
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
并发语言俨然是应大规模应用架构的需要而提出,有其现实所需。前后了解了Scala和Golang,深深体会到现代并发语言与旧有的Java、C++等语言在风格及理念上的巨大差异。本文主要针对Scala和Golang这两个我喜爱的并发语言在并发特性上的不同实现,做个比较和阐述,以进一步加深理解。
一. Scala与Golang的并发实现思路
Scala语言并发设计采用Actor模型,借鉴了Erlang的Actor实现,并且在Scala 2.10之后,Scala采用的是Akka Actor模型库。Actor模型主要特征如下:
- “一切皆是参与者”,且各个actor间是独立的;
- 发送者与已发送消息间解耦,这是Actor模型显著特点,据此实现异步通信;
- actor是封装状态和行为的对象,通过消息交换进行相互通信,交换的消息存放在接收方的邮箱中;
- actor可以有父子关系,父actor可以监管子actor,子actor唯一的监管者就是父actor;
- 一个actor就是一个容器,它包含了状态、行为、一个邮箱(邮箱用来接受消息)、子actor和一个监管策略;
- goroutine(协程,Go的轻量级线程)是Go的轻量级线程管理机制,用“go”启动一个goroutine, 如果当前线程阻塞则分配一个空闲线程,如果没有空闲线程,则新建一个线程;
- 通过管道(channel)来存放消息,channel在goroutine之间传递消息;比如通过读取channel里的消息(通俗点说好比一个个“值”),你能够明白某个goroutine里的任务完成以否;
- Go给channel做了增强,可带缓存。
- actor是异步的,因为发送者与已发送消息间实现了解耦;而channel则是某种意义上的同步,比如channel的读写是有关系的,期间会依赖对方来决定是否阻塞自己;
- actor是一个容器,使用actorOf来创建Actor实例时,也就意味着需指定具体Actor实例,即指定哪个actor在执行任务,该actor必然要有“身份”标识,否则怎么指定呢?!而channel通常是匿名的, 任务放进channel之后你不用关心是哪个channel在执行任务;
我们来看一个例子:对一组连续序列(1-10000)的整数值进行累加,分别观察Scala与Go环境下单线程与多线程效率,一方面了解并发效率的提升;一方面也能够对比Scala与Go并发实现的差异 ── 这才是本文的重点。具体要求如下:
对1 - 10000的整数进行累加,在并发条件下,我们将1 - 10000平均划分为四部分,启动四个线程进行并发计算,之后将四个线程的运行结果相加得出最终的累加统计值。为了更明显地观察到时间上的差异性,在每部分的每次计算过程中,我们添加一个3000000次的空循环:)
三. Scala实现
以下先列出Scala Akka Actor并发实现的完整示例代码:
// Akka并发计算实例
import akka.actor.Actor
import akka.actor.Props
import akka.actor.ActorSystem
import akka.routing.RoundRobinPool
// 定义一个case类
sealed trait SumTrait
case class Result(value: Int) extends SumTrait
// 计算用的Actor
class SumActor extends Actor {
val RANGE = 10000
def calculate(start: Int, end: Int, flag : String): Int = {
var cal = 0
for (i <- (start to end)) {
for (j <- 1 to 3000000) {}
cal += i
}
println("flag : " + flag + ".")
return cal
}
def receive = {
case value: Int =>
sender ! Result(calculate((RANGE / 4) * (value - 1) + 1, (RANGE / 4) * value, value.toString))
case _ => println("未知 in SumActor...")
}
}
// 打印结果用的Actor
class PrintActor extends Actor {
def receive = {
case (sum: Int, startTime: Long) =>
println("总数为:" + sum + ";所花时间为:"
+ (System.nanoTime() - startTime)/1000000000.0 + "秒。")
case _ => println("未知 in PrintActor...")
}
}
// 主actor,发送计算指令给SumActor,发送打印指令给PrintActor
class MasterActor extends Actor {
var sum = 0
var count = 0
var startTime: Long = 0
// 声明Actor实例,nrOfInstances是pool里所启routee(SumActor)的数量,
// 这里用4个SumActor来同时计算,很Powerful。
val sumActor = context.actorOf(Props[SumActor]
.withRouter(RoundRobinPool(nrOfInstances = 4)), name = "sumActor")
val printActor = context.actorOf(