如何修复2个通道相互死锁
How to fix 2 channels dead lock each other
我有这段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
}
它总是这样打印:
default
ch1
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox970110849/prog.go:22 +0xa0
我还尝试了这个:
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()
}
这次错误更多:
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
我哪里出错了,如何解决?
您的 main()
函数尝试在所有通道上发送,并且只有一次尝试在单独的并发 goroutine 中从这些通道读取。这是否会成功取决于 goroutine 调度程序。如果 f2()
中的非阻塞接收比 main()
中的发送更早安排,那么 main()
中较晚的发送将永远阻塞(没有人会尝试从 [=17= 中接收) ])。
摆脱死锁的一种方法是使用接收操作而不是非阻塞接收(在 Go Playground 上尝试):
func f1() {
<-ch1
fmt.Println("ch1")
}
func f2() {
<-ch2
fmt.Println("ch2")
}
因此,无论 main()
何时到达要在这些通道上发送值的点,总会有一个接收器准备好继续,因此 main()
不会卡住。
请注意,当 main()
returns 时,应用程序结束时,它不会等待非主 goroutines 完成。因此,您可能看不到控制台上打印的 ch1
和 ch2
。有关详细信息,请参阅 。
如果您的意图是等待所有 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()
}
在此处查看更多示例:; and 。
另一种选择是为通道提供 1 的缓冲区,因此 main()
可以在它们上发送 1 个值,而无需准备好从通道接收的接收器(在 Go Playground):
var ch1 = make(chan int, 1)
var ch2 = make(chan int, 1)
有了这个 main()
可以在没有 f1()
和 f2()
的情况下继续进行,所以同样,不能保证你会看到任何打印出来的东西。
Channels used here are unbuffered channels.
- 当主 goroutines 启动时,它会创建两个新的 goroutine f1 和 f2。
- 当执行 f1 或 f2 时,它会检查通道中是否有值,否则它会打印默认消息并退出。
- 在理想情况下,通道将首先发布值,然后通过 goroutine 接收它
- 实际情况是,goroutines 正在通过打印 default case 退出,但 main goroutine 试图在通道中发布值,但由于没有接收者,它面临死锁情况。
Removed deadlock from first example,
Refer the link:https://play.golang.org/p/6RuQQwC9JkA
但是,如果主例程退出,所有关联的goroutines将被自动杀死。
这就是为什么我们可以看到该值是任意打印的。
Removed deadlock from Second example,
Refer the link:https://play.golang.org/p/yUmc_jjZMgV
sync.WaitGroup 正在让 main goroutine 等待,这就是为什么我们可以在每次程序执行时观察输出。
我有这段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
}
它总是这样打印:
default
ch1
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/tmp/sandbox970110849/prog.go:22 +0xa0
我还尝试了这个:
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()
}
这次错误更多:
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
我哪里出错了,如何解决?
您的 main()
函数尝试在所有通道上发送,并且只有一次尝试在单独的并发 goroutine 中从这些通道读取。这是否会成功取决于 goroutine 调度程序。如果 f2()
中的非阻塞接收比 main()
中的发送更早安排,那么 main()
中较晚的发送将永远阻塞(没有人会尝试从 [=17= 中接收) ])。
摆脱死锁的一种方法是使用接收操作而不是非阻塞接收(在 Go Playground 上尝试):
func f1() {
<-ch1
fmt.Println("ch1")
}
func f2() {
<-ch2
fmt.Println("ch2")
}
因此,无论 main()
何时到达要在这些通道上发送值的点,总会有一个接收器准备好继续,因此 main()
不会卡住。
请注意,当 main()
returns 时,应用程序结束时,它不会等待非主 goroutines 完成。因此,您可能看不到控制台上打印的 ch1
和 ch2
。有关详细信息,请参阅
如果您的意图是等待所有 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()
}
在此处查看更多示例:
另一种选择是为通道提供 1 的缓冲区,因此 main()
可以在它们上发送 1 个值,而无需准备好从通道接收的接收器(在 Go Playground):
var ch1 = make(chan int, 1)
var ch2 = make(chan int, 1)
有了这个 main()
可以在没有 f1()
和 f2()
的情况下继续进行,所以同样,不能保证你会看到任何打印出来的东西。
Channels used here are unbuffered channels.
- 当主 goroutines 启动时,它会创建两个新的 goroutine f1 和 f2。
- 当执行 f1 或 f2 时,它会检查通道中是否有值,否则它会打印默认消息并退出。
- 在理想情况下,通道将首先发布值,然后通过 goroutine 接收它
- 实际情况是,goroutines 正在通过打印 default case 退出,但 main goroutine 试图在通道中发布值,但由于没有接收者,它面临死锁情况。
Removed deadlock from first example, Refer the link:https://play.golang.org/p/6RuQQwC9JkA
但是,如果主例程退出,所有关联的goroutines将被自动杀死。 这就是为什么我们可以看到该值是任意打印的。
Removed deadlock from Second example, Refer the link:https://play.golang.org/p/yUmc_jjZMgV
sync.WaitGroup 正在让 main goroutine 等待,这就是为什么我们可以在每次程序执行时观察输出。