Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tag: 0.1
Fetching contributors…

Cannot retrieve contributors at this time

120 lines (110 sloc) 5.294 kb
\epi{%
\begin{itemize}
\item{Parallelism is about performance;}
\item{Concurrency is about program design.}
\end{itemize}%
}{\textit{Google IO 2010}\\\textsc{Robe Pike}}
\noindent{}In this chapter we will show off Go's ability for
concurrent programming using channels and goroutines. Goroutines
are the central entity in Go's ability for concurrency. But what
\emph{is} a goroutines? From \cite{effective_go}:
\begin{quote}
They're called goroutines because the existing terms --- threads, coroutines,
processes, and so on --- convey inaccurate connotations. A goroutine has a simple
model: \emph{it is a function executing in parallel with other goroutines in the same
address space}. It is lightweight, costing little more than the allocation of
stack space. And the stacks start small, so they are cheap, and grow by
allocating (and freeing) heap storage as required.
\end{quote}
The following idea for a program was taken from \cite{curso_go}.
We run a function as two goroutines, the goroutines wait for an amount of
time and them print something to the screen. The \func{main} function
waits long enough, so that both goroutines will have printed their text. Right
now we wait for 5 seconds (\func{time.Sleep()} counts in ns) on line 18, but in fact we have no idea how
long we should wait until all goroutines have exited.
If we did not wait for the goroutines (i.e. remove line 18) the program
would be terminated immediately and any running goroutines would die with it.
\lstinputlisting[numbers=right,label=src:sleeping,caption=Go routines in action,linerange={8,18}]{src/sleep.go}
What we need here is some kind of mechanism which allows us to
communicate with the goroutines. This mechanism is available
to us in the form of \first{channels}{channels}. A
\first{channel}{channel} can be
compared to a two-way pipe in Unix shells. You can send to and receive
values from it. Those values can only be of a specific type: the
type of the channel. If we define a channel, we must also define the
type of the values we can send on the channel:
\begin{lstlisting}
ci := make(chan int)
cs := make(chan string)
cf := make(chan interface{})
\end{lstlisting}
Makes \var{ci} a channel on which we can send and receive integers,
makes \var{cs} a channel for strings and \var{cf} a channel for types
that satisfy the empty interface.
Sending on a channel and receiving from it, is done with the same operator:
\lstinline{<-}. Depending on the operands it figures out what to do:
\begin{lstlisting}
ci <- 1 |\coderemark{\emph{Send} the integer 1 to the channel \var{ci}}|
<-ci |\coderemark{\emph{Receive} an integer from the channel \var{ci}}|
i := <-ci |\coderemark{\emph{Receive} from the channel \var{ci} and storing it in \var{i}}|
\end{lstlisting}
Lets put this to use.
\begin{lstlisting}[numbers=right,caption=Go routines and a channel,label=src:sleeping with channels]
var c chan int |\longremark{Declare \var{c} to be a variable that is a %
channel of \type{int}s. That is: this channel can move integers. Note %
that this variable is global so that the goroutines have access to it;}|
func ready(w string, sec int) {
time.Sleep(int64(sec) * 1e9)
fmt.Println(w, "is ready!")
c <- 1 |\longremark{Send the integer 1 on the channel \var{c};}|
}
func main() {i
c = make(chan int) |\longremark{Initialize \var{c}, note that we must use \func{make} here;}|
go ready("Tee", 2) |\longremark{Start the goroutines as usual;}|
go ready("Coffee", 1)
fmt.Println("I'm waiting, but not too long")
<-c |\longremark{Wait until we receive a value from the channel. Note that the value we receive is discarded;}|
<-c |\longremark{Two goroutines, two values to receive.}|
}
\end{lstlisting}
\showremarks
There is still some remaining ugliness; we have to read twice from
the channel (lines 14 and 15). This is OK in this case, but what if
we don't know how many goroutines we started? This is where another
Go built-in comes in: \first{\key{select}}{keyword!select}. With \key{select} you
can (among other things) listing for incoming data on a channel.
Using \key{select} in our program does not really make it shorter,
because we run too few goroutines. We remove the lines 14 and 15 and
replace them with the following:
\begin{lstlisting}[caption=Using \key{select},numbers=right,firstnumber=14]
L: for {
select {
case <-c:
i++
if i > 1 {
break L
}
}
}
\end{lstlisting}
\todo{block 'n stuff, close 'n closed}
\subsection{Make it run in parallel}
While our goroutines were running concurrent, they were not running in
parallel. When you do not tell Go anything there can only be one
goroutine running at a time. With \func{runtime.GOMAXPROCS(n)} you
can set the number of goroutines that can run in parallel. From
the documentation:
\begin{quote}
GOMAXPROCS sets the maximum number of CPUs that can be executing
simultaneously and returns the previous setting. If n < 1, it does not
change the current setting. \emph{This call will go away when the scheduler
improves.}
\end{quote}
If you do not want to change any source code you can also set an
environment variable \verb|GOMAXPROCS| to the desired value.
\section{Exercises}
\input{ex-channels/ex-for-channels.tex}
\input{ex-channels/ex-fib.tex}
\cleardoublepage
\section{Answers}
\shipoutAnswer
Jump to Line
Something went wrong with that request. Please try again.