MST

星途 面试题库

面试题:Go chan在并发场景下的死锁分析与解决

假设有这样一个场景,多个goroutine通过chan进行数据交互,然而程序偶尔会出现死锁现象。请分析可能导致死锁的原因有哪些,并说明如何利用Go的工具(如`go tool pprof`等)来诊断和解决这类死锁问题。给出一个可能会出现死锁的代码示例,并阐述修复思路和方法。
44.1万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能导致死锁的原因

  1. 发送方和接收方不匹配:只有发送方而没有接收方,或者只有接收方而没有发送方,导致通道阻塞。例如在无缓冲通道中,如果先发送数据但没有接收方,或者先接收但没有发送方,就会出现死锁。
  2. 通道关闭不当:在接收方还没接收完数据时关闭通道,或者在发送方还可能发送数据时重复关闭通道,可能导致死锁。
  3. 循环依赖:多个goroutine之间通过通道形成循环依赖,例如A等待B发送数据,B等待C发送数据,而C又等待A发送数据。

利用Go工具诊断死锁问题

  1. go tool pprof:虽然go tool pprof主要用于性能分析,但结合runtime/pprof包可以在程序运行过程中收集goroutine状态等信息来辅助诊断死锁。首先在程序中引入runtime/pprof包,然后在程序开始处调用pprof.StartCPUProfile开启CPU profiling,在需要分析死锁处,比如程序出现异常时,调用pprof.Lookup("goroutine").WriteTo将goroutine信息输出到文件,之后利用go tool pprof工具对该文件进行分析,查看各goroutine的状态,找到死锁相关的线索。
  2. go build -race:Go的竞态检测工具,在构建程序时使用go build -race,运行程序时如果出现死锁,它会输出详细的死锁信息,包括哪些goroutine参与了死锁以及它们的操作,定位死锁位置非常方便。

死锁代码示例

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    go func() {
        ch <- 10 // 发送数据,但没有接收方
    }()
    // 这里没有接收操作,程序会立即退出,导致发送操作死锁
}

修复思路和方法

  1. 增加接收方:在上述示例中,在main函数中增加对通道数据的接收操作。
package main

import (
    "fmt"
)

func main() {
    ch := make(chan int)
    go func() {
        ch <- 10
    }()
    val := <-ch
    fmt.Println(val)
}
  1. 使用带缓冲通道:如果发送操作比较频繁,而接收操作可能相对滞后,可以使用带缓冲通道,在一定程度上避免死锁。例如ch := make(chan int, 10),这样即使暂时没有接收方,发送方也可以在缓冲区满之前继续发送数据。但要注意合理设置缓冲区大小,避免过度占用内存。