MST

星途 面试题库

面试题:Go语言defer语句在并发资源释放及竞争控制中的应用

假设有一个Go程序,在多个goroutine中并发访问和操作共享资源(如共享内存、文件等),每个goroutine在操作完成后都需要释放相关资源(例如关闭文件句柄、解锁互斥锁等)。请设计一个通用的方案,利用defer语句实现资源的正确释放,同时要考虑到并发环境下可能出现的竞争条件,详细说明如何通过defer语句与其他同步机制(如sync包中的工具)配合来避免资源泄漏和数据竞争问题。
45.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
  1. 使用sync.Mutex保护共享资源
    • 对于共享内存,使用sync.Mutex来防止多个goroutine同时访问和修改。
    • 示例代码如下:
package main

import (
    "fmt"
    "sync"
)

var (
    sharedData int
    mu         sync.Mutex
)

func worker(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    defer mu.Unlock()
    // 操作共享资源
    sharedData++
    fmt.Printf("goroutine modified sharedData to %d\n", sharedData)
}
  • 在上述代码中,worker函数在操作共享资源sharedData前获取mu互斥锁,操作完成后通过defer mu.Unlock()释放锁,确保在任何时刻只有一个goroutine能操作sharedData,避免数据竞争。
  1. 文件操作的资源释放
    • 当操作文件这类共享资源时,使用defer来关闭文件句柄。
    • 示例代码如下:
package main

import (
    "fmt"
    "io/ioutil"
    "sync"
)

func readFile(wg *sync.WaitGroup) {
    defer wg.Done()
    file, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Printf("Error reading file: %v\n", err)
        return
    }
    defer func() {
        // 这里可以对文件关闭进行错误处理,例如在更复杂场景下记录日志等
        _ = file.Close()
    }()
    // 处理文件内容
    fmt.Printf("File content: %s\n", file)
}
  • readFile函数中,defer确保在函数结束时文件句柄会被关闭,防止资源泄漏。
  1. 使用sync.WaitGroup等待所有goroutine完成
    • 为了确保所有goroutine都完成操作并释放资源后程序才退出,使用sync.WaitGroup
    • 示例代码结合前面两个示例:
package main

import (
    "fmt"
    "io/ioutil"
    "sync"
)

var (
    sharedData int
    mu         sync.Mutex
)

func worker(wg *sync.WaitGroup) {
    defer wg.Done()
    mu.Lock()
    defer mu.Unlock()
    // 操作共享资源
    sharedData++
    fmt.Printf("goroutine modified sharedData to %d\n", sharedData)
}

func readFile(wg *sync.WaitGroup) {
    defer wg.Done()
    file, err := ioutil.ReadFile("test.txt")
    if err != nil {
        fmt.Printf("Error reading file: %v\n", err)
        return
    }
    defer func() {
        // 这里可以对文件关闭进行错误处理,例如在更复杂场景下记录日志等
        _ = file.Close()
    }()
    // 处理文件内容
    fmt.Printf("File content: %s\n", file)
}

func main() {
    var wg sync.WaitGroup
    numWorkers := 3
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go worker(&wg)
    }
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go readFile(&wg)
    }
    wg.Wait()
    fmt.Println("All goroutines completed.")
}
  • main函数中,通过wg.Add(1)为每个goroutine增加计数,wg.Done()在每个goroutine结束时减少计数,wg.Wait()等待所有goroutine完成,保证所有资源都能正确释放。

通过上述sync.Mutexsync.WaitGroup等同步机制与defer语句配合,能够有效避免资源泄漏和数据竞争问题,实现对共享资源在并发环境下的安全操作。