通道并发问题
Channels concurrency issue
我正在试验 Go 中的通道概念。我写了下面的程序 playground 来实现使用通道的计数器。但是我没有得到任何输出,尽管我在 goroutine 主体中进行了一些打印。
func main() {
wg := sync.WaitGroup{}
ch := make(chan int)
count := func(ch chan int) {
var last int
last = <-ch
last = last + 1
fmt.Println(last)
ch <- last
wg.Done()
}
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
}
我希望至少有一些输出,但我得到的是 none。
当 main()
函数(main
goroutine)结束时,您的程序也会结束(它不会等待其他非 main
goroutine 完成)。您必须在末尾添加一个 wg.Wait()
调用。参见 。
一旦你这样做,你就会陷入僵局。这是因为所有的 goroutines 都是从尝试从通道接收一个值开始的,然后才会发送一些东西。
所以你应该先在频道上发送一些东西让至少一个 goroutines 继续。
执行此操作后,您会看到数字打印 10 次,然后再次出现死锁。这是因为当最后一个 goroutine 尝试发送其递增的数字时,将没有人接收到它。解决这个问题的一种简单方法是为通道提供缓冲区。
最终的工作示例:
wg := sync.WaitGroup{}
ch := make(chan int, 2)
count := func(ch chan int) {
var last int
last = <-ch
last = last + 1
fmt.Println(last)
ch <- last
wg.Done()
}
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
go func() {
ch <- 0
}()
wg.Wait()
输出(在 Go Playground 上尝试):
1
2
3
4
5
6
7
8
9
10
另请注意,由于我们对通道进行了缓冲,因此没有必要使用另一个 goroutine 来发送初始值,我们可以在 main
goroutine 中这样做:
ch <- 0
wg.Wait()
这将输出相同的结果。在 Go Playground.
上试用
func main() {
wg := sync.WaitGroup{}
ch := make(chan int)
count := func(ch chan int) {
var last int
last, ok := <-ch // 这里做一层保护
if !ok {
return
}
last = last + 1
fmt.Println(last)
go func(ch chan int, res int) {
ch <- res
}(ch, last)
wg.Done()
}
go func() {
ch <- 0
}()
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
wg.Wait()
fmt.Println("main finish")
close(ch)
}
我正在试验 Go 中的通道概念。我写了下面的程序 playground 来实现使用通道的计数器。但是我没有得到任何输出,尽管我在 goroutine 主体中进行了一些打印。
func main() {
wg := sync.WaitGroup{}
ch := make(chan int)
count := func(ch chan int) {
var last int
last = <-ch
last = last + 1
fmt.Println(last)
ch <- last
wg.Done()
}
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
}
我希望至少有一些输出,但我得到的是 none。
当 main()
函数(main
goroutine)结束时,您的程序也会结束(它不会等待其他非 main
goroutine 完成)。您必须在末尾添加一个 wg.Wait()
调用。参见
一旦你这样做,你就会陷入僵局。这是因为所有的 goroutines 都是从尝试从通道接收一个值开始的,然后才会发送一些东西。
所以你应该先在频道上发送一些东西让至少一个 goroutines 继续。
执行此操作后,您会看到数字打印 10 次,然后再次出现死锁。这是因为当最后一个 goroutine 尝试发送其递增的数字时,将没有人接收到它。解决这个问题的一种简单方法是为通道提供缓冲区。
最终的工作示例:
wg := sync.WaitGroup{}
ch := make(chan int, 2)
count := func(ch chan int) {
var last int
last = <-ch
last = last + 1
fmt.Println(last)
ch <- last
wg.Done()
}
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
go func() {
ch <- 0
}()
wg.Wait()
输出(在 Go Playground 上尝试):
1
2
3
4
5
6
7
8
9
10
另请注意,由于我们对通道进行了缓冲,因此没有必要使用另一个 goroutine 来发送初始值,我们可以在 main
goroutine 中这样做:
ch <- 0
wg.Wait()
这将输出相同的结果。在 Go Playground.
上试用func main() {
wg := sync.WaitGroup{}
ch := make(chan int)
count := func(ch chan int) {
var last int
last, ok := <-ch // 这里做一层保护
if !ok {
return
}
last = last + 1
fmt.Println(last)
go func(ch chan int, res int) {
ch <- res
}(ch, last)
wg.Done()
}
go func() {
ch <- 0
}()
wg.Add(10)
for i := 1; i <= 10; i++ {
go count(ch)
}
wg.Wait()
fmt.Println("main finish")
close(ch)
}