具有多个goroutine和一个通道的死锁

时间:2018-12-30 01:23:22

标签: go

我有一个无法解决的死锁问题。

我拥有一些网址,goroutine中的每个网址都会带给我足够的数据。我将这些数据放在一个通道中。但是,如果我关闭通道,程序将无法运行,如果我离开通道,则会出现死锁。

我不知道该怎么解决,有人会解决的

下面我以简化的方式提出问题

package main

import (
    "fmt"
)

type urlNumbers struct {
    url string
    numbers []int
}

func getNumbers(urls []urlNumbers) chan int {
    ch := make(chan int)

    for _, url := range urls {
        go allNumbersOfURL(url, ch)
    }

    return ch
}

func allNumbersOfURL(url urlNumbers, ch chan int) {
    for _, i := range url.numbers {
        ch <- i
    }
}

func main() {
    url1 := urlNumbers {url: "1", numbers: []int{1, 2, 3}}
    url2 := urlNumbers {url: "2", numbers: []int{4, 5, 6}}
    url3 := urlNumbers {url: "3", numbers: []int{7, 8, 9}}
    url4 := urlNumbers {url: "4", numbers: []int{10, 11, 12}}

    c := getNumbers([]urlNumbers{url1, url2, url3, url4})

    for i := range c {
        fmt.Println(i)
    }

    fmt.Println("END")

}

输出

go run app.go
10
11
12
4
7
1
2
3
5
6
8
9
fatal error: all goroutines are asleep - deadlock!

2 个答案:

答案 0 :(得分:2)

您正在使用for i := range c遍历一个通道,但是代码不知道何时停止。频道上的range等待频道关闭或永久挂起。这就是为什么会出现僵局。

在通过该通道发布所有“ URL”之后,您应该关闭您的通道。可以通过sync.WaitGroup的帮助来实现。在getNumbers的循环之前,您可以使用WaitGroup并设置要等待的作业数为len(urls)

wg:=&sync.WaitGroup{}
wg.Add(len(urls))

并在返回ch之前添加一个新的goroutine:

go func() {
    wg.Wait()
    close(ch)
} ()

然后在allNumbersOfURL中,将WaitGroup添加为新参数,并在循环后设置完成的工作。

func allNumbersOfURL(url urlNumbers, ch chan int,wg *sync.WaitGroup) {
    for _, i := range url.numbers {
        ch <- i
    }
    wg.Done()
}

游乐场:https://play.golang.org/p/--7x7eXIzP9

答案 1 :(得分:2)

通道必须关闭,您可以使用sync.WaitGroup等待任务完成。这是对功能getNumbers

的修改
func getNumbers(urls []urlNumbers) <-chan int {
    ch := make(chan int)

    wg := &sync.WaitGroup{}
    for _, url := range urls {
        wg.Add(1)
        go func(url urlNumbers, ch chan<- int) {
            defer wg.Done()
            allNumbersOfURL(url, ch)
        }(url, ch)
    }

    go func(wg *sync.WaitGroup, ch chan int) {
        wg.Wait()
        close(ch)
    }(wg, ch)

    return ch
}

另外,我建议您在传递参数时使用通道方向。

  

可选的<-运算符指定通道方向,发送或   接收。如果没有给出方向,则该信道是双向的。