[GO channel deadlock]在处理通道数据时遇到死锁问题无法对应到通道是在哪个场景下被阻塞。 代码不难,只为搞懂阻塞。

自己的分析:

  • 在第一个协程执行完成后才进行关闭通道的操作所以死锁不是关闭通道导致的。
  • 在主线程上有通过forr,对通道内的数据进行消费
  • 基于下面通道阻塞的各个场景,我无法判断下面的代码是在哪个场景下被阻塞了?

Go

1. 运行环境

go version go1.19.3 linux/amd64

2. 问题描述?

package main

import (
“fmt”
“sync”
)

var locker sync.Mutex

func test(num int, ch chan int) {
for i := (num - 1) * 30; i < num*30; i++ {
if i%2 == 0 {
locker.Lock()
ch <- i
locker.Unlock()
}
}
}

func main() {

var ch chan int = make(chan int)
for i := 1; i <= 1; i++ {
    go test(i, ch)
}
defer close(ch)

for v := range ch {
    fmt.Println(v)
}

}

3. 您期望得到的结果?

No deadlock after execute above codes.

4. 您实际得到的结果?

24
26
28
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
/nfs/site/disks/simcloud_zhoudo1x_002/goproject/goroutine/main.go:28 +0x11e
exit status 2

讨论数量: 6

一个channel不能解决的问题那就用两个~~:smirk:

package main

import (
    "fmt"
    "time"
)

func test(num int, ch chan int, counter chan struct{}) {
    for i := (num - 1) * 30; i < num*30; i++ {
        if i%2 == 0 {
            ch <- i
        }
    }
    counter <- struct{}{}
}

func main() {

    var count int
    var ch chan int = make(chan int, 1)
    var counter chan struct{} = make(chan struct{}, 4)

    timerc := time.NewTimer(2 * time.Second)

    for i := 1; i <= 4; i++ {
        go test(i, ch, counter)
    }

OUTFOR:
    for {
        timerc.Reset(2 * time.Second)
        select {
        case tmp := <-ch:

            fmt.Println(tmp)
        case <-counter:

            count++
        case <-timerc.C:

            if count == 4 {
                break OUTFOR
            }
        }
    }

    close(ch)
    close(counter)
}
1年前 评论
ision (楼主) 1年前

简化下楼主代码:

package main

import (
    "fmt"
    "sync"
)

var locker sync.Mutex

func test(ch chan int) {
    fmt.Println("test start")
    for i := 0; i < 10; i++ {
        if i%2 == 0 {
            locker.Lock()
            ch <- i
            locker.Unlock()
        }
    }
    fmt.Println("test end")
}

func main() {

    var ch chan int = make(chan int)
    go test(ch)
    defer close(ch)

    for v := range ch {
        fmt.Println(v)
    }
}

执行:

❯ go run demo1.go
test start
0
2
4
6
test end
8
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /tmp/demo1.go:28 +0xff
exit status 2

重点关注这句 fatal error: all goroutines are asleep - deadlock!

test函数所在goroutines 已经执行完退出,只剩下main goroutines ,而这个唯一的goroutines 又阻塞了,这不就死锁了。

代码再简化下直接把test部分删去,也是一样的效果。

1年前 评论
wang22 1年前
ision (楼主) 1年前
func main() {
    var ch = make(chan int, 5)
    defer close(ch)

    go test(ch)

    select {
    case v := <-ch:
        fmt.Println(v)
    }
}
1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!