Common pitfalls when using goroutines

Table of contents:

Introduction

package mainimport (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello")
}
func main() {
go hello()
time.Sleep(1 * time.Second)
fmt.Println("main function")
}
$ go run hello.go
Hello
main function

Part 1: Waiting………

package mainimport (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello")
}
func main() {
go hello()
// time.Sleep(1 * time.Second) // now it's commented out
fmt.Println("main function")
}
$ go run hello.go
main function
package mainimport (
"fmt"
"sync"
)
func hello(wgrp *sync.WaitGroup) {
fmt.Println("Hello")
wgrp.Done() /////// notifies the waitgroup that it finished
}
func main() {
var wg sync.WaitGroup
wg.Add(1) /////// adds an entry to the waitgroup counter
go hello(&wg)
wg.Wait() ////// blocks execution until the goroutine finishes
fmt.Println("main function")
}
$ time go run hello.go 
Hello
main function
real 0m0.230s
user 0m0.240s
sys 0m0.099s

Part 2: Deadlocks

fatal error: all goroutines are asleep - deadlock!
package mainimport (
"fmt"
"sync"
)
func hello(wgrp *sync.WaitGroup) {
fmt.Println("Hello")
wgrp.Done() /////// removing the wgrp.Done will cause a deadlock
}
func main() {
var wg sync.WaitGroup
wg.Add(2) ///// 2 as the value will cause a deadlock
go hello(&wg)
wg.Wait() ////// blocks execution until the goroutine finishes
fmt.Println("main function")
}
package mainimport "fmt"func main() {
c := make(chan string)
c <- "hello"
fmt.Println(<-c)
}
package mainimport (
"fmt"
)
func main() {
c := make(chan string)
go func() {
get := <-c
fmt.Println("get value:", get)
}()

fmt.Println("push to channel c")
c <- "hello" // send the value and wait until it's received.
}

Part 3: Unexpected results

package mainimport (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
players := []string{"James Harden", "Kyrie Irving", "Kevin Durant"}
wg.Add(len(players))
for _, player := range players {
go func() {
fmt.Printf("printing player %s\n", player)
wg.Done()
}()
}
wg.Wait()
}
$ go run hello.go
printing player Kevin Durant
printing player Kevin Durant
printing player Kevin Durant
package mainimport (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
players := []string{"James Harden", "Kyrie Irving", "Kevin Durant"}
wg.Add(len(players))
for _, player := range players {
go func(baller string) { // add the current iterated player
fmt.Printf("printing player %s\n", baller)
wg.Done()
}(player) // add the current iterated player
}
wg.Wait()
}
$ go run hello.go
printing player James Harden
printing player Kevin Durant
printing player Kyrie Irving

Part 4: Race conditions and sharing data between goroutines

package mainimport (
"fmt"
"sync"
)
var (
mutex sync.Mutex
balance int
)
func init() {
balance = 100
}
func deposit(val int, wg *sync.WaitGroup) {
mutex.Lock() // lock
balance += val
mutex.Unlock() // unlock
wg.Done()
}
func withdraw(val int, wg *sync.WaitGroup) {
mutex.Lock() // lock
balance -= val
mutex.Unlock() // unlock
wg.Done()
}
func main() {
var wg sync.WaitGroup
wg.Add(3)
go deposit(20, &wg)
go withdraw(80, &wg)
go deposit(40, &wg)
wg.Wait()
fmt.Printf("Balance is: %d\n", balance)
}
$ go run mutex.go
Balance is: 80
package mainimport (
"fmt"
"sync"
)
var (
balance int
)
func init() {
balance = 100
}
func deposit(val int, wg *sync.WaitGroup, ch chan bool) {
ch <- true
balance += val
<-ch
wg.Done()
}
func withdraw(val int, wg *sync.WaitGroup, ch chan bool) {
ch <- true
balance -= val
<-ch
wg.Done()
}
func main() {
var wg sync.WaitGroup
ch := make(chan bool, 1) // create the channel
wg.Add(3)
go deposit(20, &wg, ch)
go withdraw(80, &wg, ch)
go deposit(40, &wg, ch)
wg.Wait()
fmt.Printf("Balance is: %d\n", balance)
}
$ go run buf.go
Balance is: 80

--

--

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