Communicating Sequential Processes (CSP)
Communicating Sequential Processes (CSP) is a formal language for describing patterns of interaction in concurrent systems. It is a model for concurrency where independent processes communicate by sharing data through channels.
1. Core Concepts
- Processes: Independent units of execution (like threads, but often lighter). They do not share memory.
- Channels: The pipes through which processes communicate. A process writes to a channel, and another process reads from it.
- Synchronization: Communication is the synchronization point. If a process tries to write to a channel, it blocks until another process is ready to read (and vice-versa), unless the channel is buffered.
2. Go (Golang) Implementation
Go's concurrency model is heavily based on CSP.
- Goroutines: Lightweight processes managed by the Go runtime.
- Channels: Typed conduits for sending and receiving values.
package main
import "fmt"
func worker(id int, jobs <-chan int, results chan<- int) {
for j := range jobs {
fmt.Println("worker", id, "started job", j)
results <- j * 2
fmt.Println("worker", id, "finished job", j)
}
}
func main() {
jobs := make(chan int, 100)
results := make(chan int, 100)
// Start 3 workers (Processes)
for w := 1; w <= 3; w++ {
go worker(w, jobs, results)
}
// Send 5 jobs (Communication via Channel)
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
// Collect results
for a := 1; a <= 5; a++ {
<-results
}
}
3. CSP vs. Actor Model
Both models deal with concurrency via message passing, but they differ in focus.
| Feature | CSP (Go, Clojure) | Actor Model (Akka, Erlang) |
|---|---|---|
| Focus | The Channel (transport) | The Actor (entity) |
| Coupling | Processes are anonymous; they only know the channel. | Actors know the identity (address) of other actors. |
| Communication | Synchronous (usually blocking) | Asynchronous (Fire and Forget) |
| Mailbox | No (unless buffered channel) | Yes (each actor has a mailbox) |
4. Benefits
- No Shared Memory: Avoids the complexity of locks, mutexes, and race conditions associated with shared memory concurrency. "Do not communicate by sharing memory; instead, share memory by communicating."
- Reasoning: It is often easier to reason about data flow through channels than complex state mutations in shared objects.
- Composability: Channels can be passed around as first-class citizens.
5. The Select Statement (Go)
The select statement is a powerful control structure in Go that lets a goroutine wait on multiple communication operations. It acts like a switch statement, but for channels.
- Non-Blocking: It blocks until one of its cases can run, then it executes that case.
- Random Selection: If multiple cases are ready, one is chosen at random.
- Timeouts: Can be used to implement timeouts for channel operations.
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
case <-time.After(1 * time.Second):
fmt.Println("timeout")
}
programming/actor-model programming/asynchronous-programming programming/distributed-systems