Scala 并发编程笔记

Posted by izumo on October 4, 2017

本文是对《Scala并发编程》一书的阅读笔记。

Future

Future[T] {
  => T
}

Future[T].foreach(f: (T) => Unit)设定结果正确返回之后要执行的操作;用Future[T].failed.foreach(f: (T) => Unit)设定结果返回失败时要执行的操作。

Try

Try[T] {
  => T
}

Try[T]或者返回正确的值 Success[T],或者返回失败Failure[Throwable]

a match {
  case Success(i) => println(i)
  case Failure(e) => println(e.getMessage)
}

同理,用Try[T].foreach(f: (T) => Unit)设定结果正确返回之后要执行的操作;用Try[T].failed.foreach(f: (T) => Unit)设定结果返回失败时要执行的操作。

Promise

val t = Promise[T] // 设置一个空的Promise[T]对象
// 以下两个操作只能执行一次,否则抛出异常
p.success[T](f: => T) // 完善Promise[T]对象
p.failure(t: => Throwable) // 完善Promise[T]对象,但是令其返回失败
// 以下两个操作可以执行任意次,但只有第一次可以执行成功
p.trySuccess[T](f: => T): Boolean // 尝试完善Promise[T]对象,返回是否执行成功
p.tryFailure(t: => Throwable) // 尝试完善Promise[T]对象,但是令其返回失败,返回是否执行成功

使用p.future返回Future[T]对象,之后的操作同Future。

Scala Async

compile group: 'org.scala-lang.modules', name: 'scala-async_2.12', version: '0.9.7'

async用于开启一个新线程,相当于Future[T].apply(f: => T);await用于在线程内等待一个Future[T]的返回值T,不会阻塞基础线程(因为是在新线程内等待)。

async {
  // do something
  await { // Future[T] }
  // do something
}

数据并行集合

集合的继承顺序

  • SeqMapSet继承自Iterable

  • ParSeqParMapParSet继承自ParIterable

  • IterableParIterable继承自GenIterable

  • SeqMapSetParSeqParMapParSet对应继承自GenSeqGenMapGenSet(例如,可以使用GenSeq同时操作SeqParSeq

  • GenSeqGenMapGenSet继承自GenIterable

在JVM中度量性能

Java字节码在JVM中运行时,首先会使用解释模式。只有当JVM确定这些字节码被执行的次数足够多时,才会将其编译为机器码,在处理器中直接执行它们。

因此,在测试代码稳定性能时,应事先运行多次。

使用并行集合的注意事项

非可并行化集合

并行集合使用Splitter[T]代表的分离器,并提供并行操作。Splitter[T]继承自Iterator[T],同样拥有hasNestnext方法;同时提供split方法可以将分离器S分解为遍历分离器S部分内容的分离器序列。

def split: Seq[Spliter[T]]

该方法允许多个独立的处理器遍历输入集合的各个组成部分。

许多Scala集合的操作都是可以并行化的,包括:ArrayArrayBufferHashMapHashSetRangeVector。它们调用.par后,会创建一个新的并行集合,该集合会与原集合共享相同的基础数据集,无需复制任何元素,而且转换速度非常快。

其它的集合属于非可并行化集合,调用.par方法时,需要将元素复制到新集合中。

非可并行化操作

有些操作天生具有顺序性,而且语义也不允许以并行的方式执行它们,如foldLeft(虽然并行集合存在这些方法,但这些方法并不能产生预期的效果)。此时应使用aggregate方法,可以减少并行操作的运行时间。

并行操作副作用

在不使用同步机制的情况下,多个线程无法正确地修改共享内存的内容。

foreachfor操作无法获得正确的结果,如果是要进行计数操作,则应使用count方法(且性能更优)。

不确定的并行操作

多线程程序会有不确定性,相同的输入,因其执行语句的次序,会有不同的输出结果。如find方法,语义上是返回第一个匹配成功的元素,但多线程中,集合片段执行的顺序不确定,因此返回的元素也不确定。

此时应使用indexWhere方法。

只要并行集合操作的操作符是纯函数,那么除find之外的其他并行集合操作就都是确定的。