Tro*_*yvs 2 locking channel go
我有这段 Go 代码。我需要有这种能力:在一个地方写入通道,并在另一个地方读出它们(反之亦然):
package main
import "fmt"
var ch1=make(chan int)
var ch2=make(chan int)
func f1() {
select {
case <- ch1:fmt.Println("ch1")
default: fmt.Println("default")
}
}
func f2() {
select {
case <- ch2:fmt.Println("ch2")
default: fmt.Println("default")
}
}
func main() {
go f1()
go f2()
ch1<-1
ch2<-2
}
Run Code Online (Sandbox Code Playgroud)
它总是打印这样的东西:
default
ch1
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox970110849/prog.go:22 +0xa0
Run Code Online (Sandbox Code Playgroud)
此外,我试过这个:
package main
import (
"fmt"
"sync"
)
var ch1=make(chan int)
var ch2=make(chan int)
func f1() {
select {
case <- ch1:fmt.Println("ch1")
default: fmt.Println("default")
}
}
func f2() {
select {
case <- ch2:fmt.Println("ch2")
default: fmt.Println("default")
}
}
func w1() {
ch1 <-1
}
func w2() {
ch2 <-1
}
func main() {
var wg sync.WaitGroup
wg.Add(4)
go f1()
go f2()
go w1()
go w2()
wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)
这次的错误更多:
default
ch2
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [semacquire]:
sync.runtime_Semacquire(0x40e028, 0x0)
/usr/local/go/src/runtime/sema.go:56 +0x40
sync.(*WaitGroup).Wait(0x40e020, 0x14b720)
/usr/local/go/src/sync/waitgroup.go:130 +0x60
main.main()
/tmp/sandbox916639182/prog.go:36 +0x100
goroutine 8 [chan send]:
main.w1()
/tmp/sandbox916639182/prog.go:23 +0x40
created by main.main
/tmp/sandbox916639182/prog.go:34 +0xc0
Run Code Online (Sandbox Code Playgroud)
我哪里出错了,如何解决?
您的main()函数尝试在所有通道上发送,并且只有一次尝试在单独的并发 goroutine 中从这些通道读取。如果这会成功,这取决于 goroutine 调度程序。如果非阻塞接收f2()比 send in 更早安排main(),那么后面的 send inmain()将永远阻塞(没有人会ch2再次尝试接收)。
摆脱死锁的一种方法是使用接收操作而不是非阻塞接收(在Go Playground上尝试):
func f1() {
<-ch1
fmt.Println("ch1")
}
func f2() {
<-ch2
fmt.Println("ch2")
}
Run Code Online (Sandbox Code Playgroud)
因此,无论何时main()到达在这些通道上发送值的点,总会有一个接收器准备好继续,因此main()不会卡住。
请注意,当main()返回时,应用程序结束,它不会等待非主 goroutines 完成。因此,您可能不会在控制台上看到ch1和ch2打印。有关详细信息,请参阅Go 中的 goroutine 无输出。
如果您的意图是在您的应用程序存在之前等待所有 goroutine 完成它们的工作,请使用sync.WaitGroup它(在Go Playground上尝试):
var ch1 = make(chan int)
var ch2 = make(chan int)
var wg sync.WaitGroup
func f1() {
defer wg.Done()
<-ch1
fmt.Println("ch1")
}
func f2() {
defer wg.Done()
<-ch2
fmt.Println("ch2")
}
func main() {
wg.Add(1)
go f1()
wg.Add(1)
go f2()
ch1 <- 1
ch2 <- 2
wg.Wait()
}
Run Code Online (Sandbox Code Playgroud)
在此处查看更多示例:Solving goroutines deadlock;并防止 main() 函数在 Golang 中的 goroutines 完成之前终止。
另一种选择是为通道提供一个 1 的缓冲区,这样就main()可以在没有准备好从通道接收的接收器上发送 1 个值(在Go Playground上试试这个):
var ch1 = make(chan int, 1)
var ch2 = make(chan int, 1)
Run Code Online (Sandbox Code Playgroud)
有了这个,main()可以在没有f1()and的情况下继续f2(),所以同样,不能保证你会看到任何打印出来的东西。