美文网首页
go 死锁分析

go 死锁分析

作者: Newzer | 来源:发表于2024-03-25 11:58 被阅读0次

首先报错
fatal error: all goroutines are asleep - deadlock!
这说明主协程被阻塞,同时所有其他协程也阻塞或者退出,即没有在工作中的协程

package main

import (
    "context"
    "log"
)

func main() { //主协程1
    c, cancel := context.WithCancel(context.Background())
    defer cancel()

    ch1 := make(chan int)

    go func() { //协程2
        for {
            ch1 <- 2
            // fmt.Println(1111)
        }
    }()

    Start2(ch1, c) //协程3
    ch1 <- 1
    ch1 <- 2

}

// 会一直轮训执行select channel 且无法用break 退出循环
func Start1(c1 chan int, c context.Context) {
    go func() {
        for {
            select {
            case <-c.Done():
                log.Println("event manager done!")
                return
            case val, ok := <-c1:
                if !ok {
                    log.Println("event channel unexpectedly closed")
                    return
                }
                log.Println("val:", val)
            }
        }

    }()
}

// select 外面没有for 则挑选到符合条件的channel 执行一次便退出
func Start2(c1 chan int, c context.Context) {
    go func() {
        select {
        case <-c.Done():
            log.Println("event manager done!")
            return
        case val, ok := <-c1:
            if !ok {
                log.Println("event channel unexpectedly closed")
                return
            }
            log.Println("val:", val)
        }
    }()
}

由于start2没有for, 执行一次协程3便退出了,主协程和协程2都在阻塞向channel中发送数据,因此没有协程在工作了,因此会报死锁错误

将协程2里换成语句2,虽然主协程还在阻塞,但可以正常运行,因为协程2在工作

package main

import (
    "context"
    "log"
)

func main() { //主协程1
    c, cancel := context.WithCancel(context.Background())
    defer cancel()

    ch1 := make(chan int)

    go func() { //协程2
        for {
            ch1 <- 2 //语句1
            // fmt.Println(1111) //语句2
        }
    }()

    Start1(ch1, c)
    ch1 <- 1
    ch1 <- 2

}

// 会一直轮训执行select channel 且无法用break 退出循环
func Start1(c1 chan int, c context.Context) {
    go func() {
        for {
            select {
            case <-c.Done():
                log.Println("event manager done!")
                return
            case val, ok := <-c1:
                if !ok {
                    log.Println("event channel unexpectedly closed")
                    return
                }
                log.Println("val:", val)
            }
        }

    }()
}

// select 外面没有for 则挑选到符合条件的channel 执行一次便退出
func Start2(c1 chan int, c context.Context) {
    go func() {
        select {
        case <-c.Done():
            log.Println("event manager done!")
            return
        case val, ok := <-c1:
            if !ok {
                log.Println("event channel unexpectedly closed")
                return
            }
            log.Println("val:", val)
        }
    }()
}

用start2则不管协程2里是语句1还是语句2,程序都会正常运行,因为start2在不断地从channel里消费数据

通过start1和start2的对比可以看出,select + 多个case 只会选择一个符合条件的 channel 去执行,且只执行一次并退出,
要连续处理多个数据,必须外层用for循环,且这个for循环无法用break退出,必须用 return 退出函数

相关文章

网友评论

      本文标题:go 死锁分析

      本文链接:https://www.haomeiwen.com/subject/jqbdtjtx.html