Learning Go’s Concurrency Through Illustrations

Single-threaded vs. Multi-threaded Programs

func main() {
theMine := [5]string{“rock”, “ore”, “ore”, “rock”, “ore”}
foundOre := finder(theMine)
minedOre := miner(foundOre)
smelter(minedOre)
}
From Finder: [ore ore ore]From Miner: [minedOre minedOre minedOre]From Smelter: [smeltedOre smeltedOre smeltedOre]

Go routines

func main() {
theMine := [5]string{“rock”, “ore”, “ore”, “rock”, “ore”}
go finder1(theMine)
go finder2(theMine)
<-time.After(time.Second * 5) //you can ignore this for now
}
Finder 1 found ore!
Finder 2 found ore!
Finder 1 found ore!
Finder 1 found ore!
Finder 2 found ore!
Finder 2 found ore!

Channels

myFirstChannel := make(chan string)
myFirstChannel <- "hello" // Send
myVariable := <- myFirstChannel // Receive
func main() {
theMine := [5]string{“ore1”, “ore2”, “ore3”}
oreChan := make(chan string)
// Finder
go func(mine [5]string) {
for _, item := range mine {
oreChan <- item //send
}
}(theMine)
// Ore Breaker
go func() {
for i := 0; i < 3; i++ {
foundOre := <-oreChan //receive
fmt.Println(“Miner: Received “ + foundOre + “ from finder”)
}
}()
<-time.After(time.Second * 5) // Again, ignore this for now
}
Miner: Received ore1 from finderMiner: Received ore2 from finderMiner: Received ore3 from finder
bufferedChan := make(chan string, 3)
bufferedChan := make(chan string, 3)go func() {
bufferedChan <- "first"
fmt.Println("Sent 1st")
bufferedChan <- "second"
fmt.Println("Sent 2nd")
bufferedChan <- "third"
fmt.Println("Sent 3rd")
}()
<-time.After(time.Second * 1)go func() {
firstRead := <- bufferedChan
fmt.Println("Receiving..")
fmt.Println(firstRead)
secondRead := <- bufferedChan
fmt.Println(secondRead)
thirdRead := <- bufferedChan
fmt.Println(thirdRead)
}()
Sent 1st
Sent 2nd
Sent 3rd
Receiving..
first
second
third

Note: Using buffered channels doesn’t prevent blocking from happening. For example, if the finding gopher is 10 times faster than the breaker, and they communicate through a buffered channel of size 2, the finding gopher will still block multiple times in the program.

Putting it all Together

theMine := [5]string{"rock", "ore", "ore", "rock", "ore"}
oreChannel := make(chan string)
minedOreChan := make(chan string)
// Finder
go func(mine [5]string) {
for _, item := range mine {
if item == "ore" {
oreChannel <- item //send item on oreChannel
}
}
}(theMine)
// Ore Breaker
go func() {
for i := 0; i < 3; i++ {
foundOre := <-oreChannel //read from oreChannel
fmt.Println("From Finder: ", foundOre)
minedOreChan <- "minedOre" //send to minedOreChan
}
}()
// Smelter
go func() {
for i := 0; i < 3; i++ {
minedOre := <-minedOreChan //read from minedOreChan
fmt.Println("From Miner: ", minedOre)
fmt.Println("From Smelter: Ore is smelted")
}
}()
<-time.After(time.Second * 5) // Again, you can ignore this
From Finder:  oreFrom Finder:  oreFrom Miner:  minedOreFrom Smelter: Ore is smeltedFrom Miner:  minedOreFrom Smelter: Ore is smeltedFrom Finder:  oreFrom Miner:  minedOreFrom Smelter: Ore is smelted

Before you go, you should know..

// Anonymous go routine
go func() {
fmt.Println("I'm running in my own go routine")
}()
<-time.After(time.Second * 5) //Receiving from channel after 5 sec
func main() {
doneChan := make(chan string)
go func() {
// Do some work…
doneChan <- “I’m all done!”
}()

<-doneChan // block until go routine signals work is done
}
 // Ore Breaker
go func() {
for foundOre := range oreChan {
fmt.Println(“Miner: Received “ + foundOre + “ from finder”)
}
}()

Note: Ranging over a channel will block until another item is sent on the channel. The only way to stop the go routine from blocking after all sends have occurred is by closing the channel with ‘close(channel)’

myChan := make(chan string)

go func(){
myChan <- “Message!”
}()

select {
case msg := <- myChan:
fmt.Println(msg)
default:
fmt.Println(“No Msg”)
}
<-time.After(time.Second * 1)select {
case msg := <- myChan:
fmt.Println(msg)
default:
fmt.Println(“No Msg”)
}
No Msg
Message!
select {
case myChan <- “message”:
fmt.Println(“sent the message”)
default:
fmt.Println(“no message sent”)
}

Where to learn next

Google I/O 2012 — Go Concurrency Patterns

Rob Pike — ‘Concurrency Is Not Parallelism’

GopherCon 2017: Edward Muller — Go Anti-Patterns

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store