在Go语言中使用WaitGroup和Channel时为什么会出现死锁?

huangapple go评论77阅读模式
英文:

why is there deadlock when using wait group and channel in go?

问题

我想使用setter函数将0-9发送到ch1通道,然后计算函数将ch1通道中的数字平方,然后将结果发送到ch2通道。然而,当运行这段代码时,我遇到了错误。有人可以解释一下为什么会出现这种情况吗?我完全困惑了。

错误信息如下:

setter: 0
setter: 1
setter: 2
computer: 0
computer: 1
setter: 3
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000196008)
        /usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc000196000)
        /usr/local/go/src/sync/waitgroup.go:130 +0x65
main.main()
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:50 +0x13b

goroutine 18 [chan send]:
main.setter(0xc000194000, 0x200000000, 0xc000000000)
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:16 +0x107
created by main.main
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:47 +0xdb

goroutine 19 [chan send]:
main.computer(0xc000194000, 0xc000194070, 0x200000000, 0x0)
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:35 +0x11c
created by main.main
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:48 +0x12d
英文:

I wanna use setter function to send 0-9 into ch1 channel ,and computer function square the number from ch1, then send the result into ch2 channel . however, i get panic when running this code. would anybody explain why this situation happened, i totally confused.

package main

import (
	"fmt"
	"sync"
)

func setter(ch1 chan int, wg sync.WaitGroup) {
	for i:= 0; i< 10;i++ {
		fmt.Println("setter:", i)
		ch1 <- i
	}
	close(ch1)
	wg.Done()
}

func computer(ch1 chan int, ch2 chan int, wg sync.WaitGroup) {
	for true {
		tmp, ok := <- ch1
		if !ok {
			fmt.Println("computer: there is no value in ch1")
			break
		}
		fmt.Println("computer:", tmp*tmp)
		ch2 <- tmp*tmp
	}
	close(ch2)
	wg.Done()
}

func main(){
	ch1 := make(chan int,1)
	ch2 := make(chan int,1)
	var wg sync.WaitGroup
	wg.Add(2)
	go setter(ch1, wg)
	go computer(ch1, ch2, wg)

	wg.Wait()
}

the error like this :

setter: 0
setter: 1
setter: 2
computer: 0
computer: 1
setter: 3
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc000196008)
        /usr/local/go/src/runtime/sema.go:56 +0x45
sync.(*WaitGroup).Wait(0xc000196000)
        /usr/local/go/src/sync/waitgroup.go:130 +0x65
main.main()
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:50 +0x13b

goroutine 18 [chan send]:
main.setter(0xc000194000, 0x200000000, 0xc000000000)
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:16 +0x107
created by main.main
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:47 +0xdb

goroutine 19 [chan send]:
main.computer(0xc000194000, 0xc000194070, 0x200000000, 0x0)
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:35 +0x11c
created by main.main
        /Users/koujianyuan/Desktop/code/project/go/chan/communication/demo.go:48 +0x12d

答案1

得分: 1

死锁的原因是你没有从ch2通道中读取数据。当你尝试第二次往ch2通道中放入值时,ch2通道会被阻塞(第一次可以通过是因为它是一个缓冲通道,容量为1)。当往ch2通道放入值被阻塞时,它也会阻塞从ch1通道中读取值,因此setter goroutine 无法再往ch1中放入值。

由于ch1ch2通道都被阻塞,settercomputer goroutine 都无法完成,导致了死锁。

这里是一个可工作的示例。我添加了一个reader函数来从ch2通道中读取数据。

// 除此之外的代码与之前相同,只是我将函数改为使用 *sync.WaitGroup

func reader(ch chan int, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := range ch {
        fmt.Println("从通道中读取", i)
    }
    fmt.Println("reader 退出")
}

func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    var wg sync.WaitGroup
    wg.Add(3)
    go reader(ch2, &wg)
    go setter(ch1, &wg)
    go computer(ch1, ch2, &wg)

    wg.Wait()
}

这里是示例代码链接。

英文:

The reason why there is a deadlock is that you are not reading from the ch2 channel. The ch2 channel block when you try to put the value in it for the second time (the first time it passes because it is a buffered channel of 1). When putting the value in the ch2 channel blocks, it also blocks reading the value from the ch1 channel, so the setter goroutine cannot put values in ch1 anymore.

With both ch1 and ch2 channels blocked, both setter and computer goroutines cannot finish, which causes a deadlock.

Here is a working example. I've added the reader function that read from the ch2 channel.

// The rest of the code is the same except I've changed the functions to use *sync.Waitgroup

func reader(ch chan int, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := range ch {
		fmt.Println("reading from channel", i)
	}
	fmt.Println("reader exiting")
}

func main() {
	ch1 := make(chan int, 1)
	ch2 := make(chan int, 1)
	var wg sync.WaitGroup
	wg.Add(3)
	go reader(ch2, &wg)
	go setter(ch1, &wg)
	go computer(ch1, ch2, &wg)

	wg.Wait()
}

huangapple
  • 本文由 发表于 2021年12月4日 18:51:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/70224941.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定