Text file
talks/2012/concurrency.slide
1 Go Concurrency Patterns
2
3 Rob Pike
4 Google
5 https://go.dev
6
7 * Video
8
9 This talk was presented at Google I/O in June 2012.
10
11 .link http://www.youtube.com/watch?v=f6kdp27TYZs Watch the talk on YouTube
12
13 * Introduction
14
15 * Concurrency features in Go
16
17 People seemed fascinated by the concurrency features of Go when the language was first announced.
18
19 Questions:
20
21 - Why is concurrency supported?
22 - What is concurrency, anyway?
23 - Where does the idea come from?
24 - What is it good for?
25 - How do I use it?
26
27 * Why?
28
29 Look around you. What do you see?
30
31 Do you see a single-stepping world doing one thing at a time?
32
33 Or do you see a complex world of interacting, independently behaving pieces?
34
35 That's why. Sequential processing on its own does not model the world's behavior.
36
37 * What is concurrency?
38
39 Concurrency is the composition of independently executing computations.
40
41 Concurrency is a way to structure software, particularly as a way to write clean code that interacts well with the real world.
42
43 It is not parallelism.
44
45 * Concurrency is not parallelism
46
47 Concurrency is not parallelism, although it enables parallelism.
48
49 If you have only one processor, your program can still be concurrent but it cannot be parallel.
50
51 On the other hand, a well-written concurrent program might run efficiently in parallel on a multiprocessor. That property could be important...
52
53 For more on that distinction, see the link below. Too much to discuss here.
54
55 .link /s/concurrency-is-not-parallelism go.dev/s/concurrency-is-not-parallelism
56
57 * A model for software construction
58
59 Easy to understand.
60
61 Easy to use.
62
63 Easy to reason about.
64
65 You don't need to be an expert!
66
67 (Much nicer than dealing with the minutiae of parallelism (threads, semaphores, locks, barriers, etc.))
68
69 * History
70
71 To many, the concurrency features of Go seemed new.
72
73 But they are rooted in a long history, reaching back to Hoare's CSP in 1978 and even Dijkstra's guarded commands (1975).
74
75 Languages with similar features:
76
77 - Occam (May, 1983)
78 - Erlang (Armstrong, 1986)
79 - Newsqueak (Pike, 1988)
80 - Concurrent ML (Reppy, 1993)
81 - Alef (Winterbottom, 1995)
82 - Limbo (Dorward, Pike, Winterbottom, 1996).
83
84 * Distinction
85
86 Go is the latest on the Newsqueak-Alef-Limbo branch, distinguished by first-class channels.
87
88 Erlang is closer to the original CSP, where you communicate to a process by name rather than over a channel.
89
90 The models are equivalent but express things differently.
91
92 Rough analogy: writing to a file by name (process, Erlang) vs. writing to a file descriptor (channel, Go).
93
94 * Basic Examples
95
96 * A boring function
97
98 We need an example to show the interesting properties of the concurrency primitives.
99
100 To avoid distraction, we make it a boring example.
101
102 .play concurrency/support/boring.go /START/,/STOP.*/
103
104 * Slightly less boring
105
106 Make the intervals between messages unpredictable (still under a second).
107
108 .play concurrency/support/lessboring.go /START/,/STOP/
109
110 * Running it
111
112 The boring function runs on forever, like a boring party guest.
113
114 .play concurrency/support/lessboring.go /^func.main/,$
115
116 * Ignoring it
117
118 The go statement runs the function as usual, but doesn't make the caller wait.
119
120 It launches a goroutine.
121
122 The functionality is analogous to the & on the end of a shell command.
123
124 .play concurrency/support/goboring.go 1,/^}/
125
126 * Ignoring it a little less
127
128 When main returns, the program exits and takes the boring function down with it.
129
130 We can hang around a little, and on the way show that both main and the launched goroutine are running.
131
132 .play concurrency/support/waitgoboring.go /func.main/,/^}/
133
134 * Goroutines
135
136 What is a goroutine? It's an independently executing function, launched by a go statement.
137
138 It has its own call stack, which grows and shrinks as required.
139
140 It's very cheap. It's practical to have thousands, even hundreds of thousands of goroutines.
141
142 It's not a thread.
143
144 There might be only one thread in a program with thousands of goroutines.
145
146 Instead, goroutines are multiplexed dynamically onto threads as needed to keep all the goroutines running.
147
148 But if you think of it as a very cheap thread, you won't be far off.
149
150 * Communication
151
152 Our boring examples cheated: the main function couldn't see the output from the other goroutine.
153
154 It was just printed to the screen, where we pretended we saw a conversation.
155
156 Real conversations require communication.
157
158 * Channels
159
160 A channel in Go provides a connection between two goroutines, allowing them to communicate.
161
162 .code concurrency/support/helpers.go /START1/,/STOP1/
163 .code concurrency/support/helpers.go /START2/,/STOP2/
164 .code concurrency/support/helpers.go /START3/,/STOP3/
165
166 * Using channels
167
168 A channel connects the main and boring goroutines so they can communicate.
169
170 .play concurrency/support/changoboring.go /START1/,/STOP1/
171 .code concurrency/support/changoboring.go /START2/,/STOP2/
172
173 * Synchronization
174
175 When the main function executes <–c, it will wait for a value to be sent.
176
177 Similarly, when the boring function executes c <– value, it waits for a receiver to be ready.
178
179 A sender and receiver must both be ready to play their part in the communication. Otherwise we wait until they are.
180
181 Thus channels both communicate and synchronize.
182
183 * An aside about buffered channels
184
185 Note for experts: Go channels can also be created with a buffer.
186
187 Buffering removes synchronization.
188
189 Buffering makes them more like Erlang's mailboxes.
190
191 Buffered channels can be important for some problems but they are more subtle to reason about.
192
193 We won't need them today.
194
195 * The Go approach
196
197 Don't communicate by sharing memory, share memory by communicating.
198
199 * "Patterns"
200
201 * Generator: function that returns a channel
202
203 Channels are first-class values, just like strings or integers.
204
205 .play concurrency/support/generatorboring.go /START1/,/STOP1/
206 .code concurrency/support/generatorboring.go /START2/,/STOP2/
207
208 * Channels as a handle on a service
209
210 Our boring function returns a channel that lets us communicate with the boring service it provides.
211
212 We can have more instances of the service.
213
214 .play concurrency/support/generator2boring.go /START1/,/STOP1/
215
216 * Multiplexing
217
218 These programs make Joe and Ann count in lockstep.
219 We can instead use a fan-in function to let whosoever is ready talk.
220
221 .code concurrency/support/faninboring.go /START3/,/STOP3/
222 .play concurrency/support/faninboring.go /START1/,/STOP1/
223
224 * Fan-in
225
226 .image concurrency/images/gophermegaphones.jpg
227
228 * Restoring sequencing
229
230 Send a channel on a channel, making goroutine wait its turn.
231
232 Receive all messages, then enable them again by sending on a private channel.
233
234 First we define a message type that contains a channel for the reply.
235
236 .code concurrency/support/sequenceboring.go /START0/,/STOP0/
237
238 * Restoring sequencing.
239
240 Each speaker must wait for a go-ahead.
241
242 .code concurrency/support/sequenceboring.go /START1/,/STOP1/
243 .code concurrency/support/sequenceboring.go /START2/,/STOP2/
244 .play concurrency/support/sequenceboring.go /START3/,/STOP3/
245
246 * Select
247
248 A control structure unique to concurrency.
249
250 The reason channels and goroutines are built into the language.
251
252 * Select
253
254 The select statement provides another way to handle multiple channels.
255 It's like a switch, but each case is a communication:
256 - All channels are evaluated.
257 - Selection blocks until one communication can proceed, which then does.
258 - If multiple can proceed, select chooses pseudo-randomly.
259 - A default clause, if present, executes immediately if no channel is ready.
260
261 .code concurrency/support/select.go /START0/,/STOP0/
262
263 * Fan-in again
264
265 Rewrite our original fanIn function. Only one goroutine is needed. Old:
266
267 .code concurrency/support/faninboring.go /START3/,/STOP3/
268
269 * Fan-in using select
270
271 Rewrite our original fanIn function. Only one goroutine is needed. New:
272
273 .play concurrency/support/selectboring.go /START3/,/STOP3/
274
275 * Timeout using select
276
277 The time.After function returns a channel that blocks for the specified duration.
278 After the interval, the channel delivers the current time, once.
279
280 .play concurrency/support/timeout.go /START1/,/STOP1/
281
282 * Timeout for whole conversation using select
283
284 Create the timer once, outside the loop, to time out the entire conversation.
285 (In the previous program, we had a timeout for each message.)
286
287 .play concurrency/support/timeoutall.go /START1/,/STOP1/
288
289
290 * Quit channel
291
292 We can turn this around and tell Joe to stop when we're tired of listening to him.
293
294 .code concurrency/support/quit.go /START1/,/STOP1/
295 .play concurrency/support/quit.go /START2/,/STOP2/
296
297
298 * Receive on quit channel
299
300 How do we know it's finished? Wait for it to tell us it's done: receive on the quit channel
301
302 .code concurrency/support/rcvquit.go /START1/,/STOP1/
303 .play concurrency/support/rcvquit.go /START2/,/STOP2/
304
305 * Daisy-chain
306
307 .play concurrency/support/daisy.go /func/,$
308
309 * Chinese whispers, gopher style
310
311 .image concurrency/images/gophereartrumpet.jpg
312
313 * Systems software
314
315 Go was designed for writing systems software.
316 Let's see how the concurrency features come into play.
317
318 * Example: Google Search
319
320 Q: What does Google search do?
321
322 A: Given a query, return a page of search results (and some ads).
323
324 Q: How do we get the search results?
325
326 A: Send the query to Web search, Image search, YouTube, Maps, News,etc., then mix the results.
327
328 How do we implement this?
329
330 * Google Search: A fake framework
331
332 We can simulate the search function, much as we simulated conversation before.
333
334 .code concurrency/support/google.go /START2/,/STOP2/
335
336 * Google Search: Test the framework
337
338 .play concurrency/support/google.go /func.main/,/}/
339
340 * Google Search 1.0
341
342 The Google function takes a query and returns a slice of Results (which are just strings).
343
344 Google invokes Web, Image, and Video searches serially, appending them to the results slice.
345
346 .play concurrency/support/google.go /START1/,/STOP1/
347
348 * Google Search 2.0
349
350 Run the Web, Image, and Video searches concurrently, and wait for all results.
351
352 No locks. No condition variables. No callbacks.
353
354 .play concurrency/support/google2.1.go /Google/,/^}/
355
356 * Google Search 2.1
357
358 Don't wait for slow servers. No locks. No condition variables. No callbacks.
359
360 .play concurrency/support/google2.2.go /START/,/STOP/
361
362 * Avoid timeout
363
364 Q: How do we avoid discarding results from slow servers?
365
366 A: Replicate the servers. Send requests to multiple replicas, and use the first response.
367
368 .code concurrency/support/google2.3.go /START1/,/STOP1/
369
370 * Using the First function
371
372 .play concurrency/support/google2.3.go /START2/,/STOP2/
373
374 * Google Search 3.0
375
376 Reduce tail latency using replicated search servers.
377
378 .play concurrency/support/google3.0.go /START/,/STOP/
379
380 * And still…
381
382 No locks. No condition variables. No callbacks.
383
384 * Summary
385
386 In just a few simple transformations we used Go's concurrency primitives to convert a
387
388 - slow
389 - sequential
390 - failure-sensitive
391
392 program into one that is
393
394 - fast
395 - concurrent
396 - replicated
397 - robust.
398
399 * More party tricks
400
401 There are endless ways to use these tools, many presented elsewhere.
402
403 Chatroulette toy:
404
405 .link /s/chat-roulette go.dev/s/chat-roulette
406
407 Load balancer:
408
409 .link /s/load-balancer go.dev/s/load-balancer
410
411 Concurrent prime sieve:
412
413 .link /s/prime-sieve go.dev/s/prime-sieve
414
415 Concurrent power series (by McIlroy):
416
417 .link /s/power-series go.dev/s/power-series
418
419 * Don't overdo it
420
421 They're fun to play with, but don't overuse these ideas.
422
423 Goroutines and channels are big ideas. They're tools for program construction.
424
425 But sometimes all you need is a reference counter.
426
427 Go has "sync" and "sync/atomic" packages that provide mutexes, condition variables, etc. They provide tools for smaller problems.
428
429 Often, these things will work together to solve a bigger problem.
430
431 Always use the right tool for the job.
432
433 * Conclusions
434
435 Goroutines and channels make it easy to express complex operations dealing with
436
437 - multiple inputs
438 - multiple outputs
439 - timeouts
440 - failure
441
442 And they're fun to use.
443
444
445 * Links
446
447 Go Home Page:
448
449 .link / go.dev
450
451 Go Tour (learn Go in your browser)
452
453 .link /tour/ go.dev/tour
454
455 Package documentation:
456
457 .link /pkg go.dev/pkg
458
459 Articles galore:
460
461 .link /doc go.dev/doc
462
463 Concurrency is not parallelism:
464
465 .link /s/concurrency-is-not-parallelism go.dev/s/concurrency-is-not-parallelism
466
View as plain text