Si nous considérons un logiciel comme un ensemble de tâches qui sont exécutées par des processus, le défi de créer des logiciels scalables nous amène aux questions de la gestion de la concurrence et de la communication entre processus. Le modèle traditionnel de threads utilise des espaces partagés de mémoire pour communiquer et des mutex ou des sémaphores pour se synchroniser. Cependant, à grande échelle, ce modèle reste très difficile à maintenir.
Et si au lieu de partager la mémoire, nous synchronisions les processus via l’échange de messages ? Cette idée n’est pas nouvelle et constitue même le fondement du Modèle d’Acteurs et du Communicating Sequential Processes (CSP). D’ailleurs, saviez-vous que les routines du langage Go, le framework Akka et le langage Erlang sont en fait des implémentations de ces modèles ?
Si vous voulez en savoir plus, venez voir les bases de la construction d’applications hautement scalables avec des exemples d’implémentation en Go, Akka et Elixir.
2. Paper and Book
Charles Antony Richard Hoare
“A paradigm for expressing
concurrency based on
message-passing”
3. Texte ici
Communicating Sequential Processes
Agenda
● Definitions : concurrency, parallelism, etc
● How do we usually deal with concurrency?
● Communicating Sequential Processes (CSP) and Actors
● Conclusion and takeaways
8. Communicating Sequential Processes
Definitions
Process
● Behaviour pattern of an object
● Implemented by functions
● Actions http://www.designmethodsandprocesses.co.uk/
public int function(int x){
System.out.println("hello");
…..
return 1;
}
15. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
16. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
17. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
18. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
19. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
20. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
21. Texte ici
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
22. Texte ici
● How do I write in the same space of memory?
● What if a process crashes and it has a lock?
● Where do I locate this space?
Process 1 Process 2
Communicating Sequential Processes
Interaction sharing memory
28. Texte ici
● CSP
● Actor model
● Others (Amorphous computing, Flow-based programming)
Interaction by message passing
Communicating Sequential Processes
29.
30.
31. Texte ici
Reasoning
● Look around you, What do you see?
● A complex world interacting, with
independently behaving pieces
Communicating Sequential Processes
35. Texte ici
Communicating Sequential Processes
CSP (original)
Coroutine A
Coroutine B
Communication by
process name
Communication
by port
subroutine C
36. Texte ici
Communicating Sequential Processes
CSP (original)
Coroutine A
Coroutine B
Communication by
process name
Communication
by port
subroutine C
syntax?
76. func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch, 100) // Launch Generate goroutine.
Sieve(ch)
}
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
77. func Generate(ch chan<- int, number int) {
for i := 2; i < number; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
78. Texte ici
func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch, 100) // Launch Generate goroutine.
Sieve(ch)
}
ch: channel int
Communicating Sequential Processes
Explanation A channel in Go provides a
connection between two
goroutines, allowing them to
communicate.
79. Texte ici
func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch, 100) // Launch Generate goroutine.
Sieve(ch)
}
stack Generate
Communicating Sequential Processes
Explanation
ch: channel int
It's an independently
executing function,
launched by a go
statement.
80. Texte ici
func Generate(ch chan<- int, number int) {
for i := 2; i < number; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
Communicating Sequential Processes
Explanation
stack Generate
number := 100
i:=2
ch: channel int
i
2
Synchronisation, it waits for a receiver to be
ready.
81. Texte ici
func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch, 100) // Launch Generate goroutine.
Sieve(ch)
}
Communicating Sequential Processes
Explanation
stack Generate
ch: channel int
2
82. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
prime :=2
Communicating Sequential Processes
Explanation
stack Generate
ch: channel int
2
synchronization, it will wait
for a value to be sent
83. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
Communicating Sequential Processes
Explanation
stack Generate
ch: channel int
ch1: channel int
84. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
} stack Filter
Communicating Sequential Processes
Explanation
stack Generate
ch: channel int
ch1: channel int
Channels both
communicate and
synchronize
85. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
Communicating Sequential Processes
Explanation
stack Filterstack Generate
ch: channel int
ch1: channel int
86. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
prime :=...
Communicating Sequential Processes
Explanation
stack Filterstack Generate
ch: channel int
ch1 (new ch): channel int
87. Texte ici
func Generate(ch chan<- int, number int) {
for i := 2; i < number; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
Communicating Sequential Processes
Explanation
prime :=...
stack Filterstack Generate
number := 100
i:=3
ch: channel int
ch1 (new ch): channel int
i
3
88. Texte ici
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
Communicating Sequential Processes
prime :=...
stack Filter
prime:=2
stack Generate
ch(now in): channel int
ch1 (now out): channel int
i:=33
Explanation
A sender and receiver must
both be ready to play their
part in the communication.
Otherwise we wait until they
are
89. Texte ici
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
Communicating Sequential Processes
Explanation
prime :=...
stack Filter
prime:=2
stack Generate
ch(now in): channel int
ch1 (now out): channel int
i:=3
3
90. Texte ici
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
prime := 3
Communicating Sequential Processes
Explanation
stack Filterstack Generate
ch: channel int
ch1 (new ch): channel int
3
91. Texte ici
func Generate(ch chan<- int, number int) {
for i := 2; i < number; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
Communicating Sequential Processes
Explanation
stack Filterstack Generate
number := 100
i:=4
ch: channel int
ch1: channel int
i
4
92. Texte ici
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
Communicating Sequential Processes
Explanation
stack Filter
prime:=2
stack Generate
ch(now in): channel int
ch1(now out): channel int
i:=4
4
93. Texte ici
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
Communicating Sequential Processes
Explanation
stack Filter
prime:=2
stack Generate
ch(now in): channel int
ch1(now out): channel int
i:=4
94. Texte ici
And so on…..
Communicating Sequential Processes
Explanation
103. Texte ici
class Master(upper: Int) extends Actor {
var client: Option[ActorRef] = None
def receive: Receive = {
case FindPrimes =>
client = Some(sender)
val sieveRef = context.actorSelection(s"/user/${Eratosthenes.name}")
sieveRef ! Eratosthenes.Messages.Sieve(self, Nil, (2 to upper).toList)
case Result(list) =>
client.foreach(_ ! list)
}
}
Messages are not
anonymous
Communicating Sequential Processes
Explanation
104. Texte ici
class Master(upper: Int) extends Actor {
var client: Option[ActorRef] = None
def receive: Receive = {
case FindPrimes =>
client = Some(sender)
val sieveRef = context.actorSelection(s"/user/${Eratosthenes.name}")
sieveRef ! Eratosthenes.Messages.Sieve(self, Nil, (2 to upper).toList)
case Result(list) =>
client.foreach(_ ! list)
}
}
I know all actors in the system.
I can look for an actor in a path and send
messages
Communicating Sequential Processes
Explanation
106. Texte ici
class Eratosthenes extends Actor with ActorLogging {
def receive: Receive = {
case Sieve(parent, primes, remaining) =>
remaining match {
case Nil =>
parent ! Master.Messages.Result(primes)
case h :: tail =>
self ! Sieve(parent, primes ++ List(h), tail.filter { x => x % h != 0 })
}
}
}
Send message to itself with
remaining list
Explanation
108. Texte ici
class Eratosthenes extends Actor with ActorLogging {
def receive: Receive = {
case Sieve(parent, primes, remaining) =>
remaining match {
case Nil =>
parent ! Master.Messages.Result(primes)
case h :: tail =>
self ! Sieve(parent, primes ++ List(h), tail.filter { x => x % h != 0 })
}
}
}
send primes list to parent
Explanation
113. Texte ici
Building a small actor system using channels
Mixing metaphors
Process1 Actor1
Channel
114. Texte ici
Building a small actor system using channels
Mixing metaphors
Actor1Process1Channel
Actors are directly adressable. Channels are anonymous
115. Texte ici
Building a small actor system using channels
Mixing metaphors
Mailboxes are only addressable by owning actor
Actor1 Actor2
Mailbox actor1 Mailbox actor2
116. Texte ici
Building a small actor system using channels
Mixing metaphors
Channels have type
Process1Channel (Int)
117. Texte ici
Building a small actor system using channels
Mixing metaphors
In actor model, only receiver blocks
Process2ChannelProcess1
118.
119. Texte ici
● communicating haskell processes
● go
● Clojure
● Javascript(js-csp)
Communicating Sequential Processes
Who does csp?
120. Texte ici
● erlang
● elixir
● Akka
Communicating Sequential Processes
Who does actors?
123. Texte ici
Takeaways
Communicating Sequential Processes
Actors and channels are often confused, we saw a mailbox implementation through
channels to characterize them and “live” the differences
124.
125. func main() {
ch := make(chan int) // Create a new channel.
go Generate(ch, 100) // Launch Generate goroutine.
Sieve(ch)
}
func Sieve(ch chan int) {
prime := <-ch
print(prime, "n")
ch1 := make(chan int)
go Filter(ch, ch1, prime)
Sieve(ch1)
}
A channel in Go provides a
connection between two
goroutines, allowing them to
communicate.
It's an independently
executing function,
launched by a go
statement.
synchronization, it will wait
for a value to be sent
Channels both
communicate and
synchronize
channel int
stack Generate
126. func Generate(ch chan<- int, number int) {
for i := 2; i < number; i++ {
ch <- i // Send 'i' to channel 'ch'.
}
}
// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
for {
i := <-in // Receive value from 'in'.
if i%prime != 0 {
out <- i // Send 'i' to 'out'.
}
}
}
Synchronisation, it waits for a receiver to be
ready.
A sender and receiver must
both be ready to play their
part in the communication.
Otherwise we wait until they
are
127. Texte ici
Building a small actor system using channels
Mixing metaphors
Message ordering
128. Texte ici
Building a small actor system using channels
Mixing metaphors
type Actor struct {
…...
mailbox chan interface{}
}
func (actor *Actor) doSomething(message interface{}) {
select {
case actor.mailbox <- message:
default:
//throw message away
}
}
func (actor *Actor) receive(){
for{
message := <- actor.mailbox
//deal with action
…...
}
}
129. Texte ici
How many messages should we wait?
Mixing metaphors
1 message, the sender could block
∞ is not possible
n=?
132. defmodule Chan.Chan do
...
def loop do
receive do
{msg,caller} ->
cond do
is_integer(msg) -> Kernel.send(caller, msg)
true -> loop()
end
end
end
…
end
test "echo" do
{:ok, pid} = Chan.start_link()
Chan.send(pid, 1)
assert_receive 1
Chan.send(pid, :hi)
refute_receive :hi
end
133. Texte ici
Building a channel using Actors
Building a channel using Actors
defmodule Chan.Chan do
...
def loop do
receive do
{msg,caller} ->
cond do
is_integer(msg) -> Kernel.send(caller, msg)
true -> loop()
end
end
end
…
end