-
Notifications
You must be signed in to change notification settings - Fork 0
/
concurrency.tex
324 lines (246 loc) · 10.2 KB
/
concurrency.tex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
\cleardoublepage
\phantomsection
\chapter{Concurrency}
\begin{quote}
\textit{Do not communicate by sharing memory; instead, share memory by communicating.} --- Effective Go
\end{quote}
If you observe, you could see many things happening around you at any
given time. This is how the world function - the train is gently
moving, passengers talking each other, farmers working in the field
and many other things are happening simultaneously. We can say, the
world we live in function concurrently.
Go has built-in concurrency features with syntax support. The Go
concurrency is inspired by a paper published in 1978 by Tony Hoare.
The paper title is \textit{Communicating sequential
processes} \footnote{\url{http://usingcsp.com}}.
Go has some new terminologies and keywords related to concurrent
programming. The two important words are \textit{goroutine}
and \textit{channel}. This chapter will go through these concepts and
walk through some examples to further explain concurrency in Go.
The Go runtime is part of the executable binary created when compiling
any Go code. The Go runtime contains a garbage collector and a
scheduler to manage lightweight threads called Goroutines. Goroutine
is a fundamental abstraction to support concurrency. Goroutine is an
independently executing part of the program. You can invoke any
number of goroutines and all of them could run concurrently.
Goroutines can communicate to each other via typed conduits called
channels. Channels can be used to send and receive data.
\section{Goroutine}
Goroutine\index{goroutine} is like a process running in the
background. A function with \textit{go} keyword as prefix starts the
goroutine. Any function including anonymous function can be invoked
with \textit{go} keyword. In fact, the \textit{main} function is a
special goroutine invoked during the starup of any program by the Go
runtime.
To understand the Goroutine better let's look at a simple program:
\lstinputlisting{code/concurrency/goroutinesleep.go}
In the above program, \textit{setMessage} function is invoked as a
goroutine in line no 15 using the \textit{go} keyword. If you run
this program, you will get the hello world message printed. If you
change the sleep time to Zero, the message will not be printed. This
is because, the program exits when main function completes execution.
And in this case, since \textit{setMessage} is called as a goroutine,
it goes to background and main goroutine execution continues. In the
earlier case when the time sleep was 1 second, the goroutine gets some
time to execute before main completed. That's why the \textit{msg}
value is set and printed.
\section{Channels}
Multiple goroutines can communicate using channels\index{channel}.
Channels can be used to send and receive any type of values. You can
send and receive values with this channel operator: \texttt{<-}
This is how to declare a channel of \textit{int} values:
\begin{lstlisting}[numbers=none]
ch := make(chan int)
\end{lstlisting}
To send a value to \textit{ch} channel:
\begin{lstlisting}[numbers=none]
ch <- 4
\end{lstlisting}
To receive a value from \texttt{ch} channel and assign to a variable:
\begin{lstlisting}[numbers=none]
v := <-ch
\end{lstlisting}
You can also receive value without really assigning:
\begin{lstlisting}[numbers=none]
<-ch
\end{lstlisting}
Sending and receiving values from channels becomes a blocking
operation. So, if you try to receive value from a channel, there
should be some other part of the code which sends a value this
channel. Until a value sends to the channel, the receiving part of
the code will block the execution.
Here is an example:
\lstinputlisting{code/concurrency/goroutinechannel.go}
In the above example, an int channel is assigned to a global variable
named \texttt{c}. In line number 17, immediately after calling
goroutines, channel is trying to receive a value. This becomes a
blocking operation in the \texttt{main} goroutine. In line number 12,
inside the \texttt{setMessage} function, after setting a value
for \texttt{msg}, a value is send to the \texttt{c} channel. This
will make the operation to continue in the \texttt{main} goroutine.
\section{Waitgroups}
Go standard library has a \textit{sync} package which provides few
synchronization primitives. One of the mechanism
is \textit{Waitgroups}\index{waitgroup} which can be used to wait for
multiple goroutines to complete. The \texttt{Add} function add the
number of goroutines to wait for. At the end of these goroutines
call \texttt{Done} function to indicate the task has completed.
The \texttt{Wait} function call, block further operations until all
goroutines are completed.
Here is a modified version of the previous example
using \textit{Waitgroups}.
\lstinputlisting{code/concurrency/goroutinewg.go}
In the above example, the \texttt{Add} method at line number 17 make
one item to wait for. The next line invoke the goroutine. The line
number 19, the \texttt{Wait} method call blocks any further operations
until goroutines are completed. The previous line made goroutine and
inside the goroutine, at the end of that goroutine, there is
a \texttt{Done} call at line number 13.
Here is another example:
\lstinputlisting{code/concurrency/wg1.go}
\section{Select}
The \emph{select}\index{select} is a statement with some similarity
to \emph{switch}, but used with channels and goroutines.
The \emph{select} statement lets a goroutine wait on multiple
communication operations through channels.
Under a \emph{select} statement, you can add multiple cases. A select
statement blocks until one of its case is available for run -- that is
the channel has some value. If multiple channels used in cases has
value readily avaibale, select chooses one at random.
Here is an example:
\begin{lstlisting}[numbers=none]
package main
import "time"
import "fmt"
func main() {
c1 := make(chan string)
c2 := make(chan string)
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}
\end{lstlisting}
\section{Buffered Channels}
Buffered channels\index{channel!buffered} are channels with a given
capacity. The capacity is the size of channel in terms of number of
elements. If the capacity is zero or absent, the channel is
unbuffered. For a buffered channel communication succeeds only when
both a sender and receiver are ready. Whereas for a buffered channel,
communication succeeds without blocking if the buffer is not full
(sends) or not empty (receives).
The capacity can be given as the third argument to make function:
\begin{lstlisting}[numbers=none]
make(chan int, 100)
\end{lstlisting}
Consider the below example:
\begin{lstlisting}
package main
import "fmt"
func main() {
ch := make(chan string, 2)
ch <- "Hello"
ch <- "World"
fmt.Println(<-ch)
fmt.Println(<-ch)
}
\end{lstlisting}
The \textit{ch} channel is a buffered channel, this makes it possible
to send value without any receiver present.
\section{Channel Direction}
When declaring a function with channels as input parameters, you can
also specify the direction of the channel. The direction of channel
declares whether it can only receive or only send values. The channel
direction helps to increases the type-safety of the program.
Here is an example:
\lstinputlisting{code/concurrency/direction.go}
In the above example, the \texttt{sendOnly} function define a channel
variable which can be only used for sending data. If you tried to
read from that channel within that function, it's going to be compile
time error. Similary the \texttt{receiveOnly} function define a
channel variable which can be only user for receive data. You cannot
send any value to that channel from that function.
\section{Lazy Initialization Using sync.Once}
The sync\index{sync package, Once} package provide another struct called Once which
is useful for lazy initialization.
Here is an example:
\begin{lstlisting}[numbers=none]
import (
"sync"
)
type DB struct{}
var db *DB
var once sync.Once
func GetDB() *DB {
once.Do(func() {
db = &DB{}
})
return db
}
\end{lstlisting}
If the above GetDB function is called multiple times, only once the DB
object will get constructed.
\section{Exercises}
\textbf{Exercise 1:} Write a program to download a list of web pages concurrently using
Goroutines.
\noindent
Hint: Use this tool for serving junk content for testing:
\url{https://github.com/baijum/lipsum}
\textbf{Solution:}
\begin{lstlisting}[numbers=none]
package main
import (
"io/ioutil"
"log"
"net/http"
"net/url"
"sync"
)
func main() {
urls := []string{
"http://localhost:9999/1.txt",
"http://localhost:9999/2.txt",
"http://localhost:9999/3.txt",
"http://localhost:9999/4.txt",
}
var wg sync.WaitGroup
for _, u := range urls {
wg.Add(1)
go func(u string) {
defer wg.Done()
ul, err := url.Parse(u)
fn := ul.Path[1:len(ul.Path)]
res, err := http.Get(u)
if err != nil {
log.Println(err, u)
}
content, _ := ioutil.ReadAll(res.Body)
ioutil.WriteFile(fn, content, 0644)
res.Body.Close()
}(u)
}
wg.Wait()
}
\end{lstlisting}
\subsection{Additional Exercises}
Answers to these additional exercises are given in the Appendix A.
\textbf{Problem 1:} Write a program to watch log files and detect any entry with a particular word.
\section*{Summary}
This chapter explained how to use Go's concurrency features. You can choose
channels or other synchronization techniques, depending on your problem. This
chapter covered goroutines and how to use channels. It also covered Waitgroups
and Select statements. Additionally, it covered buffered channels and channel
direction. Finally, the chapter briefly discussed the \textit{sync.Once}
function.