死锁
package main
import "fmt"
func callerA(c chan string) {
c <- "Hello world! callerA"
}
func callerB(c chan string) {
c <- "Hello world! callerB"
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
for i := 0; i < 5; i++ {
select {
case msg := <-a:
fmt.Printf("%v from A
", msg)
case msg := <-b:
fmt.Printf("%v from B
", msg)
}
}
}
/*
fatal error: all goroutines are asleep - deadlock!
Hello worldBBBBBBBB! from B
Hello world! from A
goroutine 1 [select]:
main.main()
C:/Users/zhourudong/go/src/awesomeProject/main.go:18 +0x19c
*/
/*
死锁原因:
当第一次循环时,main从a通道取值后,a通道为空;
当第二次循环时,main从b通道取值后,b通道为空;
当第三次循环时,main函数发现a/b通道都为空,callerA、callerB两个goroutine都已执行完毕,
a,b通道永远不会有值,main函数无止境的等待下去,go运行环境会抛出死锁错误
*/
死锁解决
package main
import "fmt"
func callerA(c chan string) {
c <- "Hello world! callerA"
}
func callerB(c chan string) {
c <- "Hello world! callerB"
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
for i := 0; i < 5; i++ {
select {
case msg := <-a:
fmt.Printf("%v from A
", msg)
case msg := <-b:
fmt.Printf("%v from B
", msg)
default:
fmt.Println("Default")
}
}
}
/*
Default
Hello world! callerB from B
Hello world! callerA from A
Default
Default
*/
/*
输出 Default的原因是因为
通道还没有传入值,初始化完成,所以第一个只能输出Default
解决方案:
休眠
*/
// ---------------------------------------------------
package main
import (
"fmt"
"time"
)
func callerA(c chan string) {
c <- "Hello world! callerA"
}
func callerB(c chan string) {
c <- "Hello world! callerB"
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
for i := 0; i < 5; i++ {
time.Sleep(3 * time.Microsecond)
select {
case msg := <-a:
fmt.Printf("%v from A
", msg)
case msg := <-b:
fmt.Printf("%v from B
", msg)
default:
fmt.Println("Default")
}
}
}
/*
Hello world! callerA from A
Hello world! callerB from B
Default
Default
Default
*/
不使用sleep解决方案
/*
1、尝试向一个已关闭的通道发送信息会引发panic
2、尝试关闭一个已关闭的通道也会触发panic
3、尝试从一个已关闭的通道取值总得到一个与通道类型相对应的零值,
所以从已关闭的通道取值不会导致goroutine被阻塞
*/
package main
import "fmt"
func callerA(c chan string) {
// 函数被调用后关闭
c <- "Hello world callerA"
close(c)
}
func callerB(c chan string) {
// 函数被调用后关闭
c <- "Hello world callerB"
close(c)
}
func main() {
a, b := make(chan string), make(chan string)
go callerA(a)
go callerB(b)
var msg string
ok1, ok2 := true, true
for ok1 || ok2 {
select {
case msg, ok1 = <-a: // 通道取出的值会赋值给msg,ok1则表示通道已经关闭则被设置成false
if ok1 {
fmt.Printf("%s from A
", msg)
}
case msg, ok2 = <-b:
if ok2 {
fmt.Printf("%s from B
", msg)
}
}
}
}
/*
1、尝试向一个已关闭的通道发送信息会引发panic
2、尝试关闭一个已关闭的通道也会触发panic
3、尝试从一个已关闭的通道取值总得到一个与通道类型相对应的零值,
所以从已关闭的通道取值不会导致goroutine被阻塞
*/
package main
import (
"fmt"
)
func callerA(c chan int) {
// 函数被调用后关闭
c <- 1111
close(c)
}
func main() {
a := make(chan int)
go callerA(a)
var ok bool
var msg int
msg, ok = <-a // 第一次通道还没有关闭 返回值和一个布尔值,
fmt.Println(msg, ok)
msg, ok = <-a // 通道已关闭, 得到与通道相对应类型的零值,一个为false的布尔值
fmt.Println(msg, ok)
}
/*
1111 true
0 false
*/