Moore's Law may be dead, but CPUs acquire more cores every year. If you want to minimize response latency in your application, you need to use every last core - without wasting resources that would destroy performance and throughput. Traditional locks grind threads to a halt and are prone to deadlocks, and although actors provide a saner alternative, they compose poorly and are at best weakly-typed.
In this presentation, created exclusively for Scalar Conf, John reveals a new alternative to the horrors of traditional concurrency, directly comparing it to other leading approaches in Scala. Witness the beauty, simplicity, and power of declarative concurrency, as you discover how functional programming is the most practical solution to solving the tough challenges of concurrency.
20. 20
class Account {
var balance : Amount
var accountID : AccountID
var name : String
val opened : Instant
val status : AccountStatus
val tpe : AccountType
}
29. abstract class Account {
trait State {
@volatile var balance: BigDecimal = _
}
val state: State
def modify[A](f: State => A): A = this.synchronized {
val result = f(state)
this.notifyAll()
result
}
def await(): Unit = this.synchronized { this.wait() }
}
30. def transfer(from: Account, to: Account,
amount: Amount): Unit = {
var loop = true
while (loop) {
from.modify { state =>
if (state.balance >= amount.value) {
state.balance -= amount.value
to.modify(state => state.balance += amount.value)
loop = false
}
else from.await()
}
}
35. class Account extends Actor {
var balance = Amount.zero
var todos = List.empty[(ActorRef, Message)]
def receive = {
case Deposit(amount) =>
balance = balance + amount.value
sender ! Success(balance)
todos.foreach { case (s, m) => self ! m }
todos = Nil
case v @ Withdraw(amount) =>
if (balance >= amount.value) {
balance = balance - amount.value
sender ! Success(balance)
} else todos = (sender, v) :: todos
case Balance => sender ! Success(balance)
}
}
36. def transfer(from: ActorRef, to: ActorRef,
amount: Amount)(implicit garbage: Timeout): Unit =
(from ? Withdraw(amount)).flatMap { _ =>
to ? Deposit(amount)
}
38. for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$99
39. for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$6,000,000 $99
40. for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$6,000,000 $99
41. $70
~$6m
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$10 $99
42. $70
-$60
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$99
54. STM: Software Transactional Memory
Provides the ability to atomically commit a series of
reads and writes to transactional memory when a set
of conditions is satisfied.
STM
55. STM
...Op 2Op 1 Op n
Final
State
Initial
State
commit
failure - rollback
retry - rollback
complete complete
56. STM
STM[E, A]
A transaction,
which models
reads & writes,
and can fail, retry,
or succeed.
TRef[A]
A transactional
reference, which
is read & written
inside STM
transactions.
58. STM
STM[E, A]
A transaction,
which models
reads & writes,
and can fail, retry,
or succeed.
TRef[A]
A transactional
reference, which
is read & written
inside STM
transactions.
78. def checkIn(passengers: TRef[List[Person]]): STM[Nothing, Person] =
for {
head <- passengers.get.collect { case head :: tail => head }
_ <- passengers.update(_.drop(1))
} yield head
STM - Collect