SoFunction
Updated on 2025-03-05

Solve the trick of using WaitGroup in Golang

It is no stranger to Golang developers, and it is often used as a mechanism for synchronization between multiple coroutines. Using it well will definitely make you achieve twice the result with half the effort, but if you use it incorrectly, it will cause problems.

There are many examples about the use of WaitGroup on the Internet, and I won't introduce it here. What I want to say is the pitfalls I encountered when using WaitGroup in my project.

In the project, because the server has synchronization requirements, WaitGroup was used directly, but the usage scenario was not considered. As a result, after the project was launched, the client often stuttered during peak periods. After searching from multiple parties, it was found that if a separate goroutine was not started when using WaitGroup, it is very likely that the main thread would be blocked.

So I did the following test (in the test, I put WaitGroup inside the coroutine):

import (
 "fmt"
 "sync"
 "time"
)
 
func main() {
    ("main-1")
 testW()
 ("main-2")
 ((15) * ) 
}
 
func testW() {
 ("testW-1")
 go func() {
  var wg 
  ("testW-2")
  testW1(&wg)
  ("testW-5")
  ()
  ("testW-6")
 }()
}
 
func testW1(wg *) {
 (1)
 ("testW-3")
 (*5, func() {
  ()
 })
 ("testW-4")
 
}

The output is:

main-1

testchan-1

main-2

testchan-2

testchan-3

testchan-4

testchan-5

// 5 seconds later

testchan-6

Summarize:

Using WaitGroup in goroutine will not cause blockage of the main thread, and can also achieve synchronization effect.

Supplement: Simple usage of WaitGroup (wait group)

You can taste the name of someone and wait for the group. Wait for something, wait for the goroutine to complete. Sometimes, we start multiple goroutines to perform tasks, let me give you an example

listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
    //Suppose we execute the logic of ping ip    go PingIPWork(ip)
}

I have implemented a logic of pinging multiple IPs. Generally, if you execute a wave, people will definitely not return it to you. Why? Because the main thread exited directly, and the same sentence is still the case, you didn't tell the main thread to wait for all the ips to be pinged, so you have to add a wait and wait for Goroutine to complete. Here I'll give another example online

package main
import (
    "fmt"
)
func main() {
    go func() {
        ("Goroutine 1")
    }()
    go func() {
        ("Goroutine 2")
    }()
    //Go to sleep and wait for the Goroutine to end    ( * 1)
}

Did you see it? I added a sleep and used sleep to wait for Goroutine to finish running. The example I gave above can also be done like this

listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
for _, ip := range(listip) {
    //Suppose we execute the logic of ping ip    go PingIPWork(ip)
}
( * 1)

Adding a sleep can wait for completion, but if some Goroutines run fast and some slow, your sleep is only one second. If some Goroutines don't finish running, wouldn't it be useless? So we need a mechanism that can help us manage Goroutines and let us know when the Goroutine is stopped and when it is completed.

So, WaitGroup can help us solve this problem, but it is still the same. Let me give you a simple example to illustrate my idea.

package main
import (
    "fmt"
 "sync"
)
func PingIPWork(ip string) {
 (ip)
}
func main() {
    //Define a waiting for Azu var wg 
 (3) // Because there are 3 Ip, we define three actions, so we will count three times listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
 for _, ip := range(listip) {
  //Suppose we execute the logic of ping ip  go func(ip string) {
            //Execute a work            PingIPWork(ip)
            //After the operation is completed, do a count, that is, 3-1   ()
  }(ip)
 }
    //wait () // Wait until the count is 0}

Here I give a simple example. In fact, the usage of wg is relatively simple. In this example, we used it.


waitGoroutineExit the main process after it is finished

Add toGoroutine,Actually, you can think of it as,可Add to的最大Goroutinenumber

想象成销毁参number,whenGoroutineCalled after end,It means,You're gone,I'll reduce1

Other notes on WaitGroup

When passing Wg as a parameter, you need to use a pointer

Sometimes, we don’t want to write so much, so we just think about how to be simpler, or slightly stronger variability. Sometimes we have to call wg as the most parameter inside the function. How should we write it?

package main
import (
 "fmt"
 "sync"
)
func PingIPWork(ip string, wg *) {
 (ip)
 ()
}
func main() {
 var wg 
 (3) // Because there are two actions, add 2 counts listip := []string{"10.0.9.11","10.0.9.22","10.0.9.33"}
 for _, ip := range(listip) {
  //Suppose we execute the logic of ping ip  go PingIPWork(ip, &wg)
  }
 () // Wait until the count is 0}

Have you seen it? If you pass Wg as a parameter, you have to pass the value in the form of a pointer, otherwise it will be deadlocked! ! ! ! ! ! ! !

The value of the number cannot be negative

The value of () must be a positive number, and if it is a negative number, an exception will be thrown.

panic: sync: negative WaitGroup counter
goroutine 1 [running]:
sync.(*WaitGroup).Add(0xc042008230, 0xffffffffffffff9c)
    D:/Go/src/sync/:75 +0x1d0
()
    D:/code/go/src/test-src/2-Package/sync/waitgroup/:10 +0x54

The above is personal experience. I hope you can give you a reference and I hope you can support me more. If there are any mistakes or no complete considerations, I would like to give you advice.