MST

星途 面试题库

面试题:Go中死锁的常见场景及避免方法

请列举至少两种Go语言中可能导致死锁的常见场景,并说明如何避免这些场景下的死锁问题。
19.2万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

常见导致死锁的场景

  1. 双向通道通信:两个 goroutine 通过通道进行双向通信时,如果顺序不当可能死锁。例如:
package main

import "fmt"

func main() {
    ch := make(chan int)
    go func() {
        ch <- 1 // 发送数据到通道
        fmt.Println(<-ch) // 尝试从通道接收数据
    }()
    fmt.Println(<-ch) // 主 goroutine 尝试从通道接收数据
    ch <- 2 // 尝试发送数据到通道
}

在上述代码中,两个 goroutine 都先尝试接收数据,而没有一个先发送数据,导致死锁。

  1. 未缓冲通道的循环通信:在使用未缓冲通道进行循环通信时,如果发送和接收操作的次数不匹配,可能导致死锁。例如:
package main

import "fmt"

func main() {
    ch := make(chan int)
    for i := 0; i < 3; i++ {
        go func() {
            ch <- i
        }()
    }
    for i := 0; i < 2; i++ {
        fmt.Println(<-ch)
    }
}

这里有 3 个 goroutine 尝试向通道发送数据,但只有 2 次接收操作,最后一个发送操作会导致死锁。

避免死锁的方法

  1. 合理安排通信顺序:在双向通道通信场景下,确保至少有一个 goroutine 先进行发送操作。例如:
package main

import "fmt"

func main() {
    ch := make(chan int)
    go func() {
        ch <- 1 // 先发送数据
        fmt.Println(<-ch) // 再接收数据
    }()
    fmt.Println(<-ch) // 主 goroutine 接收数据
    ch <- 2 // 主 goroutine 发送数据
}
  1. 确保发送和接收操作的平衡:在使用未缓冲通道循环通信时,要保证发送和接收操作的次数一致。例如:
package main

import "fmt"

func main() {
    ch := make(chan int)
    for i := 0; i < 3; i++ {
        go func() {
            ch <- i
        }()
    }
    for i := 0; i < 3; i++ {
        fmt.Println(<-ch)
    }
}

或者可以使用带缓冲的通道,避免因接收不及时导致发送操作阻塞。例如:

package main

import "fmt"

func main() {
    ch := make(chan int, 3)
    for i := 0; i < 3; i++ {
        go func() {
            ch <- i
        }()
    }
    for i := 0; i < 2; i++ {
        fmt.Println(<-ch)
    }
}

这样前两个数据可以先存入缓冲通道,不会立即导致死锁。