在Go程序中使用select时应出现死锁
以下Go程序(扩展自,完整源代码)使用无缓冲通道:在Go程序中使用select时应出现死锁,go,select,deadlock,Go,Select,Deadlock,以下Go程序(扩展自,完整源代码)使用无缓冲通道: func service1(c chan string) { time.Sleep(3 * time.Second) fmt.Println("Ready to send on chan 1") c <- "Hello from service 1" fmt.Println("Sent on chan 1") } func service2(c
func service1(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to send on chan 1")
c <- "Hello from service 1"
fmt.Println("Sent on chan 1")
}
func service2(c chan string) {
time.Sleep(2 * time.Second)
fmt.Println("Ready to send on chan 2")
c <- "Hello from service 2"
fmt.Println("Sent on chan 2")
}
func service3(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to receive on chan 3")
res := <- c
fmt.Println("Response from service main", res, time.Since(start))
}
func main() {
fmt.Println("main() started", time.Since(start))
chan1 := make(chan string)
chan2 := make(chan string)
chan3 := make(chan string)
go service1(chan1)
go service2(chan2)
go service3(chan3)
select {
case res := <-chan1:
fmt.Println("Response from service 1", res, time.Since(start))
case res := <-chan2:
fmt.Println("Response from service 2", res, time.Since(start))
case chan3 <- "Hello from main":
fmt.Println("Sent to service 3")
}
time.Sleep(5 * time.Second)
fmt.Println("main() stopped", time.Since(start))
}
我本以为service1
和service3
会导致死锁,因为main
中的select
语句会选择第二种情况。但是,这两个goroutine既不阻塞也不完全执行(因为最终打印不会反映在输出中)
我对的理解是,尽管对操作数进行了计算,但不应执行通道1上的接收和通道3上的发送。如果错误,请纠正,并帮助阐明这种行为。谢谢大家! select语句是一条阻塞语句,将等待至少一条语句成功求值。因此,在上面的示例中,由于
service2
的睡眠时间最少(2秒)
,因此首先执行它,然后退出select语句
现在您正在等待5秒钟。但这不会执行service1
和service3
,因为select
语句已经退出,并且service1
和service3
没有接收go例程
使用一个简单的for
循环,您可以等待所有go例程执行完毕。我已经用这个更改更新了完整的源代码。即使使用for
循环,也不会出现任何死锁,因为select语句确保在给定时间只运行一条语句。如果两条语句准备执行,则随机选择其中一条
package main
import (
"fmt"
"time"
)
var start time.Time
func init() {
start = time.Now()
}
func service1(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to send on chan 1")
c <- "Hello from service 1"
fmt.Println("Sent on chan 1")
}
func service2(c chan string) {
time.Sleep(2 * time.Second)
fmt.Println("Ready to send on chan 2")
c <- "Hello from service 2"
fmt.Println("Sent on chan 2")
}
func service3(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to receive on chan 3")
res := <-c
fmt.Println("Response from service main", res, time.Since(start))
}
func main() {
fmt.Println("main() started", time.Since(start))
chan1 := make(chan string)
chan2 := make(chan string)
chan3 := make(chan string)
go service1(chan1)
go service2(chan2)
go service3(chan3)
for i := 0; i < 3; i++ {
select {
case res := <-chan1:
fmt.Println("Response from service 1", res, time.Since(start))
case res := <-chan2:
fmt.Println("Response from service 2", res, time.Since(start))
case chan3 <- "Hello from main":
fmt.Println("Sent to service 3")
}
}
fmt.Println("main() stopped", time.Since(start))
}
主程序包
进口(
“fmt”
“时间”
)
变量开始时间。时间
func init(){
开始=时间。现在()
}
func service1(c chan字符串){
时间。睡眠(3*时间。秒)
fmt.Println(“准备发送到chan 1”)
c main只是在select语句之后退出,当main退出程序时,不管发生什么情况。此外,对于死锁,所有的goroutine都必须被阻止,但主goroutine不是。啊,我明白了!我错过了这个重要信息,即死锁是由所有被阻止的goroutine定义的。@andre_c是否存在不涉及所有goroutine的死锁成员?“在并行计算中,死锁是指组中的每个成员等待另一个成员(包括其自身)采取行动的状态…”go运行时不够聪明,无法确定某个特定的goroutine是否最终会被唤醒,事实上,这个问题在总体上可能无法解决,因为它听起来与停止问题非常相似。
package main
import (
"fmt"
"time"
)
var start time.Time
func init() {
start = time.Now()
}
func service1(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to send on chan 1")
c <- "Hello from service 1"
fmt.Println("Sent on chan 1")
}
func service2(c chan string) {
time.Sleep(2 * time.Second)
fmt.Println("Ready to send on chan 2")
c <- "Hello from service 2"
fmt.Println("Sent on chan 2")
}
func service3(c chan string) {
time.Sleep(3 * time.Second)
fmt.Println("Ready to receive on chan 3")
res := <-c
fmt.Println("Response from service main", res, time.Since(start))
}
func main() {
fmt.Println("main() started", time.Since(start))
chan1 := make(chan string)
chan2 := make(chan string)
chan3 := make(chan string)
go service1(chan1)
go service2(chan2)
go service3(chan3)
for i := 0; i < 3; i++ {
select {
case res := <-chan1:
fmt.Println("Response from service 1", res, time.Since(start))
case res := <-chan2:
fmt.Println("Response from service 2", res, time.Since(start))
case chan3 <- "Hello from main":
fmt.Println("Sent to service 3")
}
}
fmt.Println("main() stopped", time.Since(start))
}