Best Practice for Utilizing sync.WaitGroup with External Functions
In dealing with concurrency in Go, it's crucial to effectively utilize sync.WaitGroup. This article addresses a common issue that arises while passing a wait group as an argument to an external function.
Problem:
Consider the following code:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, wg) // go func(){ for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } // Print prints all numbers sent on the channel. // The function returns when the channel is closed. func Print(ch <-chan int, wg sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
In this code, a deadlock occurs at the specified line, causing the program to print only from 1 to 10 instead of reaching 11. The error stems from passing a copy of sync.WaitGroup to the Print method, which hinders the expected call to its Done method.
Solution 1:
To resolve this issue, pass a pointer to the wait group instead:
<code class="go">package main import ( "fmt" "sync" ) func main() { ch := make(chan int) var wg sync.WaitGroup wg.Add(2) go Print(ch, &wg) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) defer wg.Done() }() wg.Wait() //deadlock here } func Print(ch <-chan int, wg *sync.WaitGroup) { for n := range ch { // reads from channel until it's closed fmt.Println(n) } defer wg.Done() }</code>
Passing the address of wg ensures that the Print method calls the Done method on the wait group that is waited on in the main function.
Solution 2: Simplified Print Method
Alternatively, the Print method can be simplified by removing the WaitGroup argument, as it does not require knowledge of any waiting operations:
<code class="go">package main import ( "fmt" ) func main() { ch := make(chan int) go func() { for i := 1; i <= 11; i++ { ch <- i } close(ch) }() for n := range ch { // reads from channel until it's closed fmt.Println(n) } } </code>
In this scenario, the main goroutine directly receives the channel and prints its values without involving a wait group. This approach maintains the desired functionality and eliminates the need for WaitGroup management within the Print method.
Conclusion:
When passing sync.WaitGroup as an argument to external functions, it's essential to ensure that the function receives the correct reference to the wait group being waited on. Refactoring the function to handle wait groups directly or passing a pointer to the wait group can effectively prevent deadlock errors and ensure proper concurrency control.
The above is the detailed content of How to Avoid Deadlocks When Using sync.WaitGroup with External Functions in Go?. For more information, please follow other related articles on the PHP Chinese website!