Concurrency

&

Parallelism


Artūras Šlajus

CTO @  Tiny Lab Productions

arturas.slajus@gmail.com

What is concurrency?


What is parallelism?


How do they differ?

A deeper look

We have a task.

Dig 2 holes in the ground.

Each hole takes 10 ticks to dig.


Let's take 3 approaches:

  • sequential
  • concurrent
  • parallel

Sequential

Yay DOS!


                                1 1 1 1 1 2
0 2 4 6 8 0 2 4 6 8 0

Hole 1 XXXXXXXXXX
Context Switching X
Hole 2 XXXXXXXXXX


Total ticks: 21

The problem?

Responsiveness.

Concurrent

Yay modern OSes!


                                1 1 1 1 1 2 2 2 2
0 2 4 6 8 0 2 4 6 8 0 2 4 6

Hole 1 XXX XXX XXX X
Context Switching X X X X X X X
Hole 2 XXX XXX XXX X


Total ticks: 27

The problem?

It takes longer.

Parallel

Yay multicore CPUs!


                                1 1 1 1 1 2 2 2 2
0 2 4 6 8 0 2 4 6 8 0 2 4 6

Hole 1 XXXXXXXXXX
Hole 2 XXXXXXXXXX


Total ticks: 10

The problem?

It's hard to do.

Real life

arturas@razor:~$ irb
irb(main):001:0> a = 0
=> 0
irb(main):002:0> 10.times { Thread.new { 100.times { a += 1 } } }
=> 10
irb(main):003:0> a
=> 1000


arturas@razor:~$ scala
Welcome to Scala version 2.10.3 (OpenJDK 64-Bit Server VM, Java 1.7.0_51).

scala> import java.lang._
scala> var a = 0
a: Int = 0

scala> (0 to 9).foreach { _ => new Thread(new Runnable {
def run = (0 to 99).foreach { _ => a += 1 }
}).start }

scala> a
res1: Int = 565


How to fix it

University edition

scala> val mutex = new Object
mutex: Object = java.lang.Object@64b51464

scala> (0 to 9).foreach { _ => new Thread(new Runnable {
def run = (0 to 99).foreach { _ => mutex.synchronized { a += 1 } }
}).start }

scala> a
res10: Int = 1000


Right?

Not quite


How to avoid deadlocks


Simple.


Always lock everything in the same order.


But hard to do.

Solutions?


Futures, Promises & Execution Contexts


Actor model

Future is now


Future is a read-many placeholder for a value that might be available some time in the future.

Futures!

scala> import concurrent._, ExecutionContext.Implicits.global

scala> val listOfFutures = (0 to 9).map { _ => Future { 100 } }
listOfFutures: List[Future[Int]]

scala> val futureOfLists = Future.sequence(listOfFutures)
futureOfLists: Future[List[Int]]

scala> val futureOfSum = futureOfLists.map(_.sum)
futureOfSum: Future[Int]

scala> futureOfSum.value
res0: Option[scala.util.Try[Int]] = Some(Success(1000))


How does it work exactly?


The simple (non-parallel) version


trait MyPromise[A] {
  def complete(value: A)
  def future: MyFuture[A]
  def value: Option[A]
}

trait MyFuture[A] {
  def onComplete(f: A => Unit)
  def value: Option[A]
} 

Implementation

class MyPromiseFuture[A] extends MyPromise[A] with MyFuture[A] {
private[this] var _value = Option.empty[A]
private[this] var listeners = List.empty[A => Unit]

def future = this

def complete(value: A) = {
if (_value.isDefined)
throw new IllegalStateException("Promise is already completed!")
_value = Some(value)
listeners.foreach(_(value))
listeners = List.empty
}

def value = _value

def onComplete(f: A => Unit) = value.fold(listeners ::= f)(f)
}

but what's the point?


One of the uses is turning callback style APIs into Future APIs.


val http: Http
val p = new MyPromiseFuture[Response]
http.doRequest(...).onComplete(p.complete)
p.future

Is it better?


Not for now. But add some magic sauce...


trait MyFuture[A] {
def map[B](f: A => B): MyFuture[B]
def flatMap[B](f: A => MyFuture[B]): MyFuture[B]
def zip[B](other: MyFuture[B]): MyFuture[(A, B)]
}

And implement it...

class MyPromiseFuture[A] extends MyPromise[A] with MyFuture[A] {
def map[B](f: A => B) = {
val p = new MyPromiseFuture[B]
onComplete(f andThen p.complete)
p.future
}

def flatMap[B](f: A => MyFuture[B]) = {
val p = new MyPromiseFuture[B]
onComplete(a => f(a).onComplete(p.complete))
p.future
}

def zip[B](other: MyFuture[B]) = {
val p = new MyPromiseFuture[(A, B)]
onComplete(a => other.value.foreach(b => p.complete(a, b)))
other.onComplete(b => value.foreach(a => p.complete(a, b)))
p.future
}
}

And you can do this stuff


val f1 = http.futureRequest(...)
val f2 = http.futureRequest(...)

val final = f1.zip(f2).flatMap { case (r1, r2) =>
http.futureRequest(r1.dataA, r2.dataB)
}.map { r3 => r3.dataC }

Kind of neat, huh?

Use it right now

Scala: scala.concurrent.Future


Java: java.util.concurrent.Future

(but really just use Scala futures)


Javascript: https://github.com/FuturesJS/FuturesJS


.NET: System.Threading.Tasks.Task


PHP: https://github.com/reactphp/promise

lets do actors


Futures are nice and everything.


But we need shared mutable state.

How do we share state?


Hierarchy

Real code

import akka.actor.{ActorSystem, Props, ActorRef, Actor}

object Counter {
case class Inc(by: Int)
case object Current
case class CurrentReply(count: Int)
}

class Counter(private[this] var count: Int) extends Actor {
def receive = {
case Counter.Inc(by) => count += by
case Counter.Current => sender ! Counter.CurrentReply(count)
}
}

class Increaser(manager: ActorRef, counter: ActorRef) extends Actor {
(1 to 100).foreach { _ => counter ! Counter.Inc(1) }
counter ! Counter.Current

def receive = { case msg => manager ! msg }
}

class Manager extends Actor {
val counter = context.actorOf(Props(classOf[Counter], 0), "counter")

val children = (1 to 10).map { i => context.actorOf(
Props(classOf[Increaser], self, counter), s"increaser-$i"
) }

def receive = {
case Counter.CurrentReply(count) =>
println(s"$sender reported count: $count")
}
}

object Main {
def main(args: Array[String]) {
val system = ActorSystem("main")
system.actorOf(Props(classOf[Manager]), "manager")
Console.readLine()
}
}

Real Output

[info] Running Main 
Actor[akka://main/user/manager/increaser-3#354246439] reported count: 319
Actor[akka://main/user/manager/increaser-5#-1720444000] reported count: 365
Actor[akka://main/user/manager/increaser-6#-453137600] reported count: 509
Actor[akka://main/user/manager/increaser-1#-481790462] reported count: 601
Actor[akka://main/user/manager/increaser-2#-2132540929] reported count: 622
Actor[akka://main/user/manager/increaser-4#-448697138] reported count: 770
Actor[akka://main/user/manager/increaser-7#-1979722102] reported count: 817
Actor[akka://main/user/manager/increaser-8#-1507785331] reported count: 856
Actor[akka://main/user/manager/increaser-9#-761390070] reported count: 986
Actor[akka://main/user/manager/increaser-10#-1851044690] reported count: 1000

So that's the basics


What is nice about actors?


  • They encapsulate mutable state.
  • They run in parallel automatically.
  • They are simple to reason about.
  • They don't care where they are (e.g. remote machines).
  • They handle failure gracefully (actor supervision).
  • They can interact with non-actor code (e.g. akka typed actors).

Where do I get it?


Scala / Java: www.akka.io

Ruby: www.celluloid.io

Python: www.pykka.org

.NET: https://code.google.com/p/n-act/

PHP: not yet

Thank you



Questions?




Artūras Šlajus
CTO @ Tiny Lab Productions
arturas.slajus@gmail.com

https://github.com/arturaz/concurrency-presentation

Concurrency & Parallelism

By Artūras Šlajus

Concurrency & Parallelism

  • 1,914
Loading comments...

More from Artūras Šlajus