L7 Concurrency in Go
L7 Concurrency in Go
Concurrency in Go
CS3211 Parallel and Concurrent Programming
Outline
• Revisiting concurrency vs. parallelism
• Types of parallelism
• Amdahl’s Law
• Concurrency and communication with Go
• Goroutines, channels in Go
• The sync package
• The Go memory model
CS3211 L7 - Concurrency in Go 2
Why study concurrency?
• Not a new concept!
• Traditionally concurrency was achieved through task switching
CS3211 L7 - Concurrency in Go 3
Concurrency vs. Parallelism
Concurrency Parallelism
• Two or more tasks can start, run, • Two or more tasks can run
and complete in overlapping (execute) simultaneously, at the
time periods exact same time
• They might not be running • Tasks do not only make progress,
(executing on CPU) at the same but they also actually execute
instant simultaneously
• Two or more execution flows make
progress at the same time by
interleaving their executions or by
executing instructions (on CPU) at
exactly the same time
CS3211 L7 - Concurrency in Go 5
Programming languages with mascots!
• Go!
CS3211 L7 - Concurrency in Go 6
Go
• Programming language announced at Google in 2009
• Compiled programming language
• Statically typed
• (Partially) syntactically similar to C, but with
• Memory safety
• Garbage collection
• CSP-style concurrency
• Compilers & tools: gc, gccgo, gollvm
CS3211 L7 - Concurrency in Go 7
Concurrent designs
• Types of parallelism
• Limiting factors for parallelism
CS3211 L7 - Concurrency in Go 8
Solutions to a problem Sequential
More processes
2. More gophers are not
enough
• need more carts
CS3211 L7 - Concurrency in Go 9
*https://talks.golang.org/2012/waza.slide
Concurrent composition
• Not automatically parallel!
• However, it's automatically parallelizable!
• This can be twice as fast when running in parallel
CS3211 L7 - Concurrency in Go 10
Concurrent designs
• Three gophers in action,
but with (likely) delays Break the work into tasks: aka pipeline parallelism
• Finer-grained concurrency
• Four distinct gopher
procedures:
• load books onto cart
• move cart to incinerator
• unload cart into
incinerator
• return empty cart
• Enables different ways to
parallelize
CS3211 L7 - Concurrency in Go 11
Concurrency enabled more parallelization
• Now parallelize on the other axis
• 8 gophers
CS3211 L7 - Concurrency in Go 12
Other concurrent designs
• Design 1
• Design 2
CS3211 L7 - Concurrency in Go 13
Back to computing
• In our book transport problem, substitute:
• book pile => web content
• gopher => CPU
• cart => rendering, or networking
• incinerator => proxy, browser, or other consumer
• It becomes a concurrent design for a scalable web service
• Gophers are serving web content
CS3211 L7 - Concurrency in Go 14
Take-away points
• There are many concurrent designs
• Many ways to break the processing down
• Finer level of granularity enables our program to scale
dynamically when it runs to the amount of parallelism
possible on the program’s host
• Amdahl’s law in action!
CS3211 L7 - Concurrency in Go 15
Types of parallelism
• Task parallelism
• Do the same work faster
• Data parallelism
• Embarrassingly parallel algorithms
• Do more work in the same amount of time
CS3211 L7 - Concurrency in Go 16
Task Dependency Graph
• Can be used to visualize and evaluate the task decomposition strategy
• A directed acyclic graph:
• Node: Represent each task, node value is the expected execution time
• Edge: Represent control dependency between task
• Properties:
• Critical path length: maximum (slowest) completion time
• Degree of concurrency = Total Work / Critical Path Length
• An indication of amount of work that can be done concurrently
CS3211 L7 - Concurrency in Go 17
An example
*https://medium.com/@dustinstansbury/understanding-apache-airflows-key-concepts-a96efed52b1a
CS3211 L7 - Concurrency in Go 18
Concurrent Programming Challenges
• Finding enough concurrency
• Granularity of tasks
• Coordination and synchronization
CS3211 L7 - Concurrency in Go 19
Parallel Program: Speedup
• Measure the benefit of parallelism
• A comparison between sequential and parallel execution time
𝑻𝑻𝒃𝒃𝒃𝒃𝒃𝒃𝒃𝒃_𝒔𝒔𝒔𝒔𝒔𝒔 𝒏𝒏
𝑺𝑺𝒑𝒑 𝒏𝒏 =
𝑻𝑻𝒑𝒑 𝒏𝒏
CS3211 L7 - Concurrency in Go 20
Amdahl's Law (1967)
CS3211 L7 - Concurrency in Go 21
Amdahl’s Law: Implication
• Sequential execution time:
𝒇𝒇 × 𝑻𝑻∗ 𝒏𝒏 𝟏𝟏 − 𝒇𝒇 × 𝑻𝑻∗ 𝒏𝒏
• Parallel execution time:
𝟏𝟏 − 𝒇𝒇 × 𝑻𝑻∗ 𝒏𝒏
𝒇𝒇 × 𝑻𝑻∗ 𝒏𝒏
𝒑𝒑
𝑻𝑻∗ 𝒏𝒏 𝟏𝟏 𝟏𝟏
𝑺𝑺𝒑𝒑 𝒏𝒏 = = ≤
𝟏𝟏 − 𝒇𝒇 𝟏𝟏 − 𝒇𝒇 𝒇𝒇
𝒇𝒇 × 𝑻𝑻∗ 𝒏𝒏 + 𝑻𝑻∗ 𝒏𝒏 𝒇𝒇 +
𝒑𝒑 𝒑𝒑
CS3211 L7 - Concurrency in Go 23
Outline
• Revisiting concurrency vs. parallelism
• Types of parallelism
• Amdahl’s Law
• Concurrency and communication with Go
• Goroutines, channels in Go
• The sync package
• The Go memory model
CS3211 L7 - Concurrency in Go 25
Concurrency + Communication
• Go model – based on Communicating Sequential Processes (CSP)*
• Concurrency: structure a program by breaking it into pieces that can be
executed independently
• Communication: coordinate the independent executions
*C. A. R. Hoare: Communicating Sequential Processes (CACM 1978)
• Ideas of CSP
• Refined to process calculus
• Can be used to reason about program correctness
CS3211 L7 - Concurrency in Go 26
Abstractions in Go
Concurrency Communication
• Goroutines • Channels
• A function running independently • Goroutines can write to and read
• Spin up (start) a goroutine using from a channel
go function_name channel<-
• Run on OS threads <-channel
• select statements
CS3211 L7 - Concurrency in Go 27
Abstractions in Go
Concurrency Communication
• Goroutines • Channels
• A function running independently • Goroutines can write to and read
• Spin up (start) a goroutine using from a channel
go function_name channel<-
• Run on OS threads <-channel
• select statements
• Tasks • Dependencies
CS3211 L7 - Concurrency in Go 28
Goroutines
• Function running independently
• In the same address space as other goroutines
• Like & in shell
• Cheaper than threads
• Goroutines follow the fork-join model
CS3211 L7 - Concurrency in Go 29
Running Goroutines
• Runtime multiplexes goroutines onto OS threads
• Automatic scheduling – mapping M:N
• Decouples concurrency from parallelism
• Goroutine is a special class of coroutine (concurrent
subroutine)
• When a goroutine blocks
• but no other goroutine blocks
• Preemptable: Go’s runtime can suspend them
CS3211 L7 - Concurrency in Go 30
Goroutines example
• Line 3: the main goroutine is automatically
created and started when the process
begins
• Line 4: start a goroutine using keyword go
• Line 8: the print might never happen
because the main goroutine finishes
execution before sayHello completes
CS3211 L7 - Concurrency in Go 31
Goroutines are lightweight
• A newly minted goroutine is given a few kilobytes, which is almost
always enough
• When it isn’t, the runtime grows (and shrinks) the memory for storing the
stack automatically
• The CPU overhead averages about three cheap instructions per
function call
• It is practical to create hundreds of thousands of goroutines in the
same address space
• Goroutines are not garbage collected!
• Programmer should prevent goroutine leaks
CS3211 L7 - Concurrency in Go 32
Goroutines
• Line 12: Join point
• Line 7: The goroutine is running a closure that has closed over the
iteration variable salutation
CS3211 L7 - Concurrency in Go 34
Potential issues with shared memory
• Need to synchronize access to shared memory locations
• Similar to what we did in C++
• sync package
• Don’t rely on shared memory for memory locations that are modified
• Never modify a shared memory location
• Use channels instead of modifying shared memory
CS3211 L7 - Concurrency in Go 35
Channels
• Serves as a conduit for a stream of information
• Like the pipe (|) in shell
• Values may be passed along the channel, and then read out
downstream
• Pass a value into a chan variable, and then somewhere else in your program
read it off the channel
• No knowledge is required about the other parts of your program that
work with the channel
• A channel is a reference to a place in memory where the channel
resides
• Channels (references of channels) can be passed around your program
CS3211 L7 - Concurrency in Go 36
Channels
• Typed
• Bi-/uni- directional
• Blocking
• Write to a channel that is full waits until the channel has been emptied
• Read from a channel that is empty waits until at least one item is placed on it
• Can cause deadlocks!
CS3211 L7 - Concurrency in Go 37
Creating a channel
• Lines 4-5: declare and create
a bidirectional channel using
built-in make function
CS3211 L7 - Concurrency in Go 38
Blocking operations
• Lines 23, 26: blocking read
and write
• Line 25: ok Boolean indicates
whether the read was
• a value generated by a write, or
• a default value generated from
a closed channel
CS3211 L7 - Concurrency in Go 39
Synchronizing using channels
• Line 48: ranging over a channel
• The loop doesn’t need an exit
criteria
CS3211 L7 - Concurrency in Go 40
Buffered channel
c := make(chan rune, 4)
Blocked until
A is read
CS3211 L7 - Concurrency in Go 41
CS3211 L7 - Concurrency in Go 42
Ownership of a channel
• Owner is the goroutine that instantiates, writes, and closes a channel
• Useful when reasoning about program correctness
• Unidirectional channels
• Owners have a write-access view into the channel (chan or chan<-)
• Utilizers only have a read-only view into the channel (<-chan)
Owner should Consumer should
• Instantiate the channel • Know when a channel is closed
• Perform writes, or pass ownership to • Responsibly handle blocking for any reason
another goroutine
• Close the channel
• Encapsulate 1.-3. and expose them via a
reader channel
CS3211 L7 - Concurrency in Go 43
Ownership increases safety
• Because we’re the one initializing the channel, we remove the risk of
deadlocking by writing to a nil channel
• Because we’re the one initializing the channel, we remove the risk of
panicking by closing a nil channel
• Because we’re the one who decides when the channel gets closed,
we remove the risk of panicking by writing to a closed channel
• Because we’re the one who decides when the channel gets closed,
we remove the risk of panicking by closing a channel more than once
• We wield the type checker at compile time to prevent improper
writes to our channel
CS3211 L7 - Concurrency in Go 44
select statement
• Compose channels together in a program to form larger abstractions
• Bind together channels
• locally, within a single function or type,
• globally, at the intersection of two or more components in a system
• Help safely bring channels together with concepts like cancellations,
timeouts, waiting, and default values
• Similar in syntax with a switch block
• BUT case statements aren’t tested sequentially, and execution won’t
automatically fall through if none of the criteria are met
CS3211 L7 - Concurrency in Go 45
Behavior of select
• All channel reads and writes (case statements) are considered
simultaneously to see if any of them are ready
• populated or closed channels in the case of reads
• channels that are not at capacity in the case of writes
• The entire select statement blocks if none of the channels are ready
• Handle the following:
• Multiple channels have something to read
• There are never any channels that become ready
• We want to do something, but no channels are currently ready
CS3211 L7 - Concurrency in Go 46
Multiple channels have something to read
• Output:
c1Count: 505
c2Count: 496
• Go runtime will perform a pseudorandom uniform selection over the set of case
statements
• Each case has an equal chance of being selected
CS3211 L7 - Concurrency in Go 47
Channels are not ready
• Never ready: timeout
• Line 44: time.After returns a channel that sends the current time after a
time.Duration
• Do work while waiting: use default
CS3211 L7 - Concurrency in Go 48
For-select loop
CS3211 L7 - Concurrency in Go 49
The sync package
Regarding mutexes, the sync package implements them, but we hope
Go programming style will encourage people to try higher-level
techniques. In particular, consider structuring your program so that
only one goroutine at a time is ever responsible for a particular piece of
data.
Do not communicate by sharing memory.
Instead, share memory by communicating.
CS3211 L7 - Concurrency in Go 50
Sync package
• Used mostly in small scopes such as a
struct
• Contains
• WaitGroup: wait for a set of concurrent
operations to complete
• Synchronization primitives
• Mutex and RWMutex
• Cond
• Once
• Basic constructs
• Pool
CS3211 L7 - Concurrency in Go 51
The Go memory model
• Specifies the conditions under which reads of a variable in one
goroutine can be guaranteed to observe values produced by writes to
the same variable in a different goroutine
• Happens Before
• Synchronization of goroutines and channels
CS3211 L7 - Concurrency in Go 52
Happens before
• Within a single goroutine, reads and writes must behave as if they
executed in the order specified by the program (sequenced before)
• The execution order observed by one goroutine may differ from the
order perceived by another
• To guarantee that a read r of a variable v observes a particular write w
to v, ensure that w is the only write r is allowed to observe. That is, r
is guaranteed to observe w if both of the following hold:
• w happens before r.
• Any other write to the shared variable v either happens before w or after r.
• The happens before relation is defined as the transitive closure of the
union of the sequenced before and synchronized before relations.
CS3211 L7 - Concurrency in Go 53
Synchronized before
• The go statement that starts a new goroutine synchronized before
the goroutine's execution begins
• The exit of a goroutine is not guaranteed to be synchronized before
any event in the program
• A send on a channel is synchronized before the completion of the
corresponding receive from that channel.
• The closing of a channel is synchronized before a receive that returns
a zero value because the channel is closed
• A receive from an unbuffered channel is synchronized before the
completion of the corresponding send on that channel.
• The kth receive on a channel with capacity C is synchronized before
the (k+C)th send from that channel completes.
CS3211 L7 - Concurrency in Go 54
Send-receive Synchronized Before
• A send on a channel is synchronized before the
completion of the corresponding receive from that
channel.
CS3211 L7 - Concurrency in Go 55
Summary
• Go helps distinguish between concurrency and parallelism
• Using a different way to implement concurrency based on CSP
• Goroutines and channels
References
• “Concurrency in Go” by Katherine Cox-Buday, 2017.
• “Concurrency is not Parallelism” by Rob Pike
• https://go.dev/ref/mem
CS3211 L7 - Concurrency in Go 56