MST

星途 面试题库

面试题:Go语言defer与并发场景下的执行顺序探究

在Go语言的并发编程中,启动多个goroutine,每个goroutine中都有defer语句,且这些goroutine之间存在数据共享和同步操作(如使用mutex),请描述在这种复杂场景下defer语句的执行顺序可能会遇到哪些问题,并提出解决方案,最后通过代码示例进行说明。
40.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题

  1. 数据竞争与死锁
    • 当多个goroutine通过defer语句访问共享数据,且在defer语句执行时没有正确使用同步机制(如mutex),可能会导致数据竞争,使得程序出现不可预测的结果。
    • 若在defer语句中进行复杂的同步操作,比如嵌套锁操作,可能会因为锁的获取顺序不一致导致死锁。例如,goroutine A获取锁1,然后在defer中尝试获取锁2,而goroutine B获取锁2,然后在defer中尝试获取锁1,就可能发生死锁。
  2. 执行顺序不确定性
    • 由于goroutine的并发特性,不同goroutine中defer语句的执行顺序是不确定的。当存在数据共享时,这可能导致依赖于特定执行顺序的操作出现错误。比如,一个goroutine在defer中释放资源,另一个goroutine在defer中需要使用这个资源,如果执行顺序不当,可能导致资源已释放而无法使用。

解决方案

  1. 正确使用同步机制
    • 确保在defer语句中对共享数据的访问使用正确的同步机制,如sync.Mutex。在获取数据前锁定,操作完成后解锁,以避免数据竞争。
    • 谨慎设计锁的获取和释放逻辑,避免死锁。一种常见的方法是使用固定的锁获取顺序,比如总是先获取编号小的锁,再获取编号大的锁。
  2. 明确执行顺序
    • 使用sync.WaitGroup等同步工具来明确goroutine的执行顺序。通过WaitGroupAddDoneWait方法,可以确保所有goroutine完成特定任务后再继续执行后续代码,从而控制defer语句的执行顺序。

代码示例

package main

import (
    "fmt"
    "sync"
)

var (
    mu    sync.Mutex
    count int
)

func worker(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    defer mu.Unlock()
    count++
    fmt.Printf("Worker incremented count to %d\n", count)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 5
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(&wg)
    }
    wg.Wait()
    fmt.Printf("Final count: %d\n", count)
}

在上述代码中:

  1. 同步机制:使用sync.Mutex来保护共享变量count,确保在defer语句(解锁操作)执行前,对count的操作是安全的,避免数据竞争。
  2. 执行顺序控制:通过sync.WaitGroup来确保所有goroutine完成工作后,主函数才继续执行,从而控制了defer语句的执行顺序,避免因goroutine执行顺序不确定导致的问题。