Home  >  Article  >  Backend Development  >  How to Avoid Deadlocks When Using sync.WaitGroup with External Functions in Go?

How to Avoid Deadlocks When Using sync.WaitGroup with External Functions in Go?

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2024-11-05 22:03:02877browse

How to Avoid Deadlocks When Using sync.WaitGroup with External Functions in Go?

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!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn