Pits to watch out for when using Go language WaitGroup

  • 2020-06-01 10:03:04
  • OfStack

preface

In go language, WaitGroup is used for thread synchronization. Literally, wait means to wait, group means to group, WaitGroup means to wait for 1 group, and WaitGroup means to wait for 1 series before continuing to execute. WaitGroup 1 in Golang is a recommended practice for synchronizing goroutine. I used it for more than two years without any problems.

Until the last day a colleague threw in a strange piece of code:

1 a hole


package main
 
import (
    "log"
 
    "sync"
)
 
func main() {
    wg := sync.WaitGroup{}
 
    for i := 0; i < 5; i++ {
        go func(wg sync.WaitGroup, i int) {
            wg.Add(1)
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    }
 
    wg.Wait()
 
    log.Println("exit")
}

A glance, feel no problem.

However, the results of its operation are as follows:


2016/11/27 15:12:36 exit
[Finished in 0.7s]

Or like this:


2016/11/27 15:21:51 i:2
2016/11/27 15:21:51 exit
[Finished in 0.8s]

Or like this:


2016/11/27 15:22:51 i:3
2016/11/27 15:22:51 i:2
2016/11/27 15:22:51 exit
[Finished in 0.8s]

1 degree makes me think mac on my hand is still awake...

This problem is very easy if you understand the design purpose of WaitGroup. Because WaitGroup synchronizes with goroutine, the above code is done in goroutine Add(1) Operation. So maybe there's not enough time in these goroutine Add(1) The Wait operation has been performed.

So the code goes like this:

The second pit


package main
 
import (
    "log"
 
    "sync"
)
 
func main() {
    wg := sync.WaitGroup{}
 
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(wg sync.WaitGroup, i int) {
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    }
 
    wg.Wait()
 
    log.Println("exit")
}

However, mac fell asleep again, and died of sleep:


2016/11/27 15:25:16 i:1
2016/11/27 15:25:16 i:2
2016/11/27 15:25:16 i:4
2016/11/27 15:25:16 i:0
2016/11/27 15:25:16 i:3
fatal error: all goroutines are asleep - deadlock!

wg is passed to the copy of goroutine, resulting in only the Add operation, which is actually performed on the copy of wg. So Wait is deadlocked.

So the code goes like this:

Fill in the pit


package main
 
import (
    "log"
 
    "sync"
)
 
func main() {
    wg := &sync.WaitGroup{}
 
    for i := 0; i < 5; i++ {
        wg.Add(1)
        go func(wg *sync.WaitGroup, i int) {
            log.Printf("i:%d", i)
            wg.Done()
        }(wg, i)
    }
 
    wg.Wait()
 
    log.Println("exit")
}

conclusion

All right, so far we have solved the problem. The above are some questions about the use of Go language WaitGroup. I hope the problems mentioned in this paper will be helpful to you when you are learning or using Go language.


Related articles: