go并发: 死锁与阻塞

一,空select语句:会报死锁

代码:

// 入口函数
func main() {

    select{}
}

运行结果:

$ go run main.go 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [select (no cases)]:
main.main()
        /data/goapp/imagebank/main.go:8 +0xf
exit status 2

 二,sync.WaitGroup,协程数量不足任务数量时,会报死锁

代码:

// 入口函数
func main() {

    var wg sync.WaitGroup

    // 假设有两个任务要执行
    wg.Add(2)

    go func() {
        defer wg.Done() // 完成一个任务
        fmt.Println("任务1完成")
    }()

    /*
    go func() {
        defer wg.Done() // 完成一个任务
        fmt.Println("任务2完成")
    }()
    */
    // 阻塞,直到上面两个 goroutine 执行完
    wg.Wait()
    fmt.Println("所有任务完成")
}

我们注释掉了两个协程中的一个,

运行结果:

$ go run main.go 
任务1完成
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc0000140b0?)
        /usr/local/soft/go/src/runtime/sema.go:71 +0x25
sync.(*WaitGroup).Wait(0xc000044740?)
        /usr/local/soft/go/src/sync/waitgroup.go:118 +0x48
main.main()
        /data/goapp/imagebank/main.go:30 +0x73
exit status 2

三,主协程内读写无缓冲channel会报死锁

1,代码:

// 入口函数
func main() {

    ch := make(chan int, 0)

    ch <- 666
    x := <- ch
    fmt.Println(x)
}

说明:

当 Go 协程给一个无缓冲channel发送数据时,需要有其他 Go 协程来接收数据。
如果没有的话,程序就会在运行时触发 panic,形成死锁

运行结果:

$ go run main.go 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /data/goapp/imagebank/main.go:17 +0x36
exit status 2

2, 解决方案一:

增加缓冲即可:

// 入口函数
func main() {

    ch := make(chan int, 1)

    ch <- 666
    x := <- ch
    fmt.Println(x)
}

3,解决方案二:用select判断超时退出

// 入口函数
func main() {

    ch := make(chan int)
    select {
    case data := <-ch:
        fmt.Println("Received:", data)
    case <-time.After(2 * time.Second): // 等待 2 秒超时
        fmt.Println("Timeout, no data received")
    }
}

运行结果:

$ go run main.go 
Timeout, no data received

 

四,无缓冲channel,协程启动在写入之后,会报死锁

代码:

// 入口函数
func main() {
    ch := make(chan int,0)
    ch <- 666
    go func() {
        x := <- ch
        fmt.Println(x)
    }()
}

原因:协程启动是在写入666之后,写入时还没有协程接收数据,
所以会报错

运行结果:

$ go run main.go 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /data/goapp/imagebank/main.go:17 +0x2d
exit status 2

解决: 先启动接收的协程,再写入到channel即可

// 入口函数
func main() {
    ch := make(chan int,0)
    go func() {
        x := <- ch
        fmt.Println(x)
    }()
    ch <- 666
}

再次运行:

$ go run main.go 
666

五,有缓冲通道:无数据时读取,会报死锁:

代码:

// 入口函数
func main() {
    
    ch := make(chan int, 1)

    x := <- ch
    fmt.Println(x)
}

运行结果:

$ go run main.go 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /data/goapp/imagebank/main.go:13 +0x32
exit status 2

六,有缓冲channnel的缓存已经写满,仍然向通道写数据,但没有协程读取,会报死锁

代码:

// 入口函数
func main() {

    ch := make(chan int, 1)
    ch <- 666   //full
    ch <- 888   //panic
    fmt.Println("write finish")
}

运行结果:

$ go run main.go 
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan send]:
main.main()
        /data/goapp/imagebank/main.go:12 +0x45
exit status 2

七,向关闭的channel写入,会引发panic

// 入口函数
func main() {

    ch := make(chan int, 1)
    close(ch)
    ch <- 1 // 向已关闭的 channel 写入数据会引发 panic
}

运行结果:

$ go run main.go 
panic: send on closed channel

goroutine 1 [running]:
main.main()
        /data/goapp/imagebank/main.go:10 +0x3a
exit status 2

八,从关闭的channel读取数据:读取完后会返回0

channel关闭后不允许再写入,但仍然可以读取

例子:

// 入口函数
func main() {

    ch := make(chan int, 1)
    ch <- 42
    close(ch)
    value, ok := <-ch
    fmt.Println(value, ok) // 输出: 42 true
    value, ok = <-ch
    fmt.Println(value, ok) // 输出: 0 false
}

运行结果:

$ go run main.go 
42 true
0 false

 

posted @   刘宏缔的架构森林  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
历史上的今天:
2021-03-01 spring boot单元测试之二:用MockMvc为controller做单元测试(spring boot 2.4.3)
2020-03-01 docker的常用操作之二:docker内无法解析dns之firewalld设置等
2020-03-01 centos8安装fastdfs6.06集群方式三之:storage的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式二之:tracker的安装/配置/运行
2020-03-01 centos8安装fastdfs6.06集群方式一之:软件下载与安装
2020-03-01 fastdfs之同一台storage server下包含多个store path
2020-03-01 centos8安装fastdfs6.06(单机方式)
点击右上角即可分享
微信分享提示