MST

星途 面试题库

面试题:Go语言defer语句在并发延迟任务中的应用

假设你正在开发一个多 goroutine 的并发程序,其中每个 goroutine 都有一些需要延迟执行的任务(例如清理操作)。请描述在这种情况下使用 defer 语句可能会遇到的问题,并给出解决方案。同时,编写一段示例代码来展示你的思路。
37.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

使用defer语句可能遇到的问题

  1. 资源释放时机不确定:如果多个goroutine中都使用defer语句进行资源清理等延迟任务,由于goroutine的并发特性,无法准确预知每个defer语句执行的具体顺序,可能导致资源在不期望的时间被释放,例如在主程序还需要依赖该资源时就被释放了。
  2. 资源竞争:当多个goroutine通过defer释放共享资源(如文件描述符、数据库连接等)时,可能会出现资源竞争问题,导致程序崩溃或出现未定义行为。

解决方案

  1. 使用sync.WaitGroup:通过WaitGroup来等待所有goroutine完成任务,确保所有defer语句都执行完毕后,主程序再退出。这样可以控制资源释放的时机,避免主程序提前退出导致defer语句未执行。
  2. 使用互斥锁(sync.Mutex):对于共享资源的释放操作,使用互斥锁来保证同一时间只有一个goroutine进行资源释放,避免资源竞争。

示例代码

package main

import (
    "fmt"
    "sync"
)

func worker(id int, wg *sync.WaitGroup, mu *sync.Mutex) {
    defer wg.Done()

    // 模拟延迟执行的任务
    fmt.Printf("Worker %d started\n", id)
    mu.Lock()
    defer mu.Unlock()
    fmt.Printf("Worker %d finished\n", id)
}

func main() {
    var wg sync.WaitGroup
    var mu sync.Mutex
    numWorkers := 3

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(i, &wg, &mu)
    }

    wg.Wait()
    fmt.Println("All workers completed")
}

在上述代码中:

  1. sync.WaitGroup用于等待所有goroutine完成。每个goroutine启动时调用wg.Add(1),结束时调用wg.Done(),主程序通过wg.Wait()等待所有goroutine完成。
  2. sync.Mutex用于保护共享资源(这里虽然没有实际的共享资源操作,但模拟了可能存在资源竞争的场景),确保同一时间只有一个goroutine能执行关键部分代码,避免资源竞争。mu.Lock()mu.Unlock()配对使用,defer mu.Unlock()保证在函数结束时解锁,即使函数提前返回也能正确解锁。