Home  >  Article  >  Backend Development  >  What are the causes of deadlocks in Go code using WaitGroups and Buffered Channels?

What are the causes of deadlocks in Go code using WaitGroups and Buffered Channels?

PHPz
PHPzforward
2024-02-09 11:09:30699browse

使用 WaitGroups 和 Buffered Channels 的 Go 代码中出现死锁的原因是什么?

#php editor Baicao will answer a common question in this article: "What is the reason for deadlock in Go code using WaitGroups and Buffered Channels?" In the Go language , WaitGroups and Buffered Channels are commonly used concurrent programming tools. However, sometimes you may encounter deadlock situations in code that uses them. This article will delve into the causes of deadlocks and provide solutions to help readers avoid this problem. Whether you are a beginner or an experienced Go developer, this article will provide you with valuable information.

Question content

Waiting group, buffer channel and deadlock

This code of mine causes a deadlock, but I'm not sure why. I've tried using mutexes in a few different places, closing channels inside and outside separate go routines, but still the same result.

I try to send data through one channel (inputchan) and then read data from another channel (outputchan)

package main

import (
    "fmt"
    "sync"
)

func listStuff(wg *sync.WaitGroup, workerID int, inputChan chan int, outputChan chan int) {
    defer wg.Done()

    for i := range inputChan {
        fmt.Println("sending ", i)
        outputChan <- i
    }
}

func List(workers int) ([]int, error) {
    _output := make([]int, 0)

    inputChan := make(chan int, 1000)
    outputChan := make(chan int, 1000)

    var wg sync.WaitGroup
    wg.Add(workers)

    fmt.Printf("+++ Spinning up %v workers\n", workers)
    for i := 0; i < workers; i++ {
        go listStuff(&wg, i, inputChan, outputChan)
    }

    for i := 0; i < 3000; i++ {
        inputChan <- i
    }

    done := make(chan struct{})
    go func() {
        close(done)
        close(inputChan)
        close(outputChan)
        wg.Wait()
    }()

    for o := range outputChan {
        fmt.Println("reading from channel...")
        _output = append(_output, o)
    }

    <-done
    fmt.Printf("+++ output len: %v\n", len(_output))
    return _output, nil
}

func main() {
    List(5)
}

Solution

The code in the main function is continuous,Firsttry to write the 3k value into inputchan thenThe value will be read from outputchan.

Your code will block on the first step:

  • Nothing is drained from outputchan until 3k values ​​are successfully sent to inputchan, so the worker ends up stuck at outputchan &lt after the first 1k values ;- i
  • Once workers stop consuming resources from inputchan, main will get stuck at inputchan <- i
  • after about 2k values

One way to solve this problem is to have the producer (inputchan <- i) and the final consumer (for o := range outputchan {) in separate goroutines in operation.

You can keep one of these actors in the main goroutine and spin up a new actor for the other one. For example:

go func(inputchan chan<- int){
    for i := 0; i < 3000; i++ {
        inputchan <- i
    }
    close(inputchan)
}(inputchan)

done := make(chan struct{})
go func() {
    close(done)
    // close(inputchan) // i chose to close inputchan above, don't close it twice
    close(outputchan)
    wg.wait()
}()

...

https://www.php.cn/link/80e4c54699b5b8cf8c67dd496909fceb

An additional note: the order of operations surrounding signaling done is important; channels done and outputchan can only be used on wg.done () Instruct all workers to close when finished

// it is best to close inputChan next to the code that controls
    // when its input is complete.
    close(inputChan)
    // If you had several producers writing to the same channel, you
    // would probably have to add a separate waitgroup to handle closing,
    // much like you did for your workers

    go func() {
        wg.Wait()
        // the two following actions must happen *after* workers have
        // completed
        close(done)
        close(outputChan)
    }()

The above is the detailed content of What are the causes of deadlocks in Go code using WaitGroups and Buffered Channels?. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:stackoverflow.com. If there is any infringement, please contact admin@php.cn delete