一个通道有一个接收者和未知数量的 goroutines 发送者导致死锁
One channel with one receiver and unknown number of goroutines senders causing deadlock
我有一个频道,接收器是主要的。我生成了多个 goroutine,每个 goroutine 通过通道发送一个字符串。
现在,这会导致死锁,因为我没有使用关闭函数正确关闭通道。问题是,我不知道会创建多少 goroutine,所以无法知道何时关闭通道。
我试过使用 WaitGroup,问题是,我读到我不能在 goroutine 中使用 Add,我应该在主程序中使用 wg.Add(1) process/goroutine, 我试过在生成子 goroutine 的父 goroutine 中使用 Add,这也导致了死锁
主要包
import (
"fmt"
"sync"
)
var i = 0
func doSomething(ch chan string, wg sync.WaitGroup) {
defer wg.Done()
ch <- fmt.Sprintf("doSomething: %d", i)
i++
if i == 10 {return}
wg.Add(1)
go doSomething(ch, wg)
}
func main() {
ch := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go doSomething(ch, wg)
wg.Wait()
for s := range ch {
fmt.Println(s)
}
}
现在,这只是一个测试代码,所以,假设我们不知道我们将只创建 10 个 goroutines,假设它在运行时是未知的,这里我立即得到一个没有任何输出的死锁错误,如果我不使用 WorkGroup 在打印第 10 个字符串之前出现错误(因为我没有关闭通道)
我也试过不为每个函数调用生成一个 goroutine,而是对所有递归调用(从 main 开始)使用一个 goroutine,为了关闭通道,我为第一次调用创建了一个匿名函数doSomething 函数然后调用 close,因此所有递归调用都将被评估,我们肯定知道何时关闭通道。但是,这就是我现在想要完成的,我正在尝试让未知数量的 goroutine 一起工作,并在它们以某种方式完成后关闭通道。
有几个问题。
首先是程序在将它们作为参数传递时复制等待组值。复制时等待组不能正常工作。而是将指针传递给等待组。
第二个问题是 main 在从通道接收值之前等待所有 goroutines 完成。因为通道的缓冲区不够大,无法容纳所有发送的值,所以程序死锁。
第三个问题是主要范围在通道上方,但没有任何东西关闭通道。 Main 不会因此退出。
要解决第二个和第三个问题,启动另一个 goroutine 等待 doSomthing
s 并在完成后关闭通道。
试试这个:
func doSomething(ch chan string, wg *sync.WaitGroup) {
defer wg.Done()
ch <- fmt.Sprintf("doSomething: %d", i)
i++
if i == 10 {
return
}
wg.Add(1)
go doSomething(ch, wg)
}
func main() {
ch := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go doSomething(ch, &wg)
go func() {
wg.Wait()
close(ch)
}()
for s := range ch {
fmt.Println(s)
}
}
我有一个频道,接收器是主要的。我生成了多个 goroutine,每个 goroutine 通过通道发送一个字符串。
现在,这会导致死锁,因为我没有使用关闭函数正确关闭通道。问题是,我不知道会创建多少 goroutine,所以无法知道何时关闭通道。
我试过使用 WaitGroup,问题是,我读到我不能在 goroutine 中使用 Add,我应该在主程序中使用 wg.Add(1) process/goroutine, 我试过在生成子 goroutine 的父 goroutine 中使用 Add,这也导致了死锁
主要包
import (
"fmt"
"sync"
)
var i = 0
func doSomething(ch chan string, wg sync.WaitGroup) {
defer wg.Done()
ch <- fmt.Sprintf("doSomething: %d", i)
i++
if i == 10 {return}
wg.Add(1)
go doSomething(ch, wg)
}
func main() {
ch := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go doSomething(ch, wg)
wg.Wait()
for s := range ch {
fmt.Println(s)
}
}
现在,这只是一个测试代码,所以,假设我们不知道我们将只创建 10 个 goroutines,假设它在运行时是未知的,这里我立即得到一个没有任何输出的死锁错误,如果我不使用 WorkGroup 在打印第 10 个字符串之前出现错误(因为我没有关闭通道)
我也试过不为每个函数调用生成一个 goroutine,而是对所有递归调用(从 main 开始)使用一个 goroutine,为了关闭通道,我为第一次调用创建了一个匿名函数doSomething 函数然后调用 close,因此所有递归调用都将被评估,我们肯定知道何时关闭通道。但是,这就是我现在想要完成的,我正在尝试让未知数量的 goroutine 一起工作,并在它们以某种方式完成后关闭通道。
有几个问题。
首先是程序在将它们作为参数传递时复制等待组值。复制时等待组不能正常工作。而是将指针传递给等待组。
第二个问题是 main 在从通道接收值之前等待所有 goroutines 完成。因为通道的缓冲区不够大,无法容纳所有发送的值,所以程序死锁。
第三个问题是主要范围在通道上方,但没有任何东西关闭通道。 Main 不会因此退出。
要解决第二个和第三个问题,启动另一个 goroutine 等待 doSomthing
s 并在完成后关闭通道。
试试这个:
func doSomething(ch chan string, wg *sync.WaitGroup) {
defer wg.Done()
ch <- fmt.Sprintf("doSomething: %d", i)
i++
if i == 10 {
return
}
wg.Add(1)
go doSomething(ch, wg)
}
func main() {
ch := make(chan string)
var wg sync.WaitGroup
wg.Add(1)
go doSomething(ch, &wg)
go func() {
wg.Wait()
close(ch)
}()
for s := range ch {
fmt.Println(s)
}
}