MST

星途 面试题库

面试题:Go语言Context取消机制在并发场景下的问题及解决方案

在一个包含多个goroutine的并发程序中使用Context进行取消操作时,可能会遇到哪些问题(比如资源未正确释放等)?如何通过合理使用Context以及相关的Go语言特性来解决这些问题?请详细阐述并给出示例代码。
47.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

可能遇到的问题

  1. 资源未正确释放:如果在 goroutine 中打开了文件、连接数据库等操作,在 Context 取消时,这些资源可能没有正确关闭或释放,导致资源泄漏。
  2. goroutine 泄漏:即使 Context 取消了,但如果 goroutine 没有正确响应取消信号,它可能会继续运行,占用系统资源。
  3. 嵌套 goroutine 处理不当:当一个 goroutine 启动多个子 goroutine 时,如果没有正确传递 Context,父 goroutine 取消时,子 goroutine 可能不会收到取消信号,继续运行。

解决方法

  1. 资源释放:在 goroutine 中,使用 defer 语句来确保在函数返回时释放资源。例如,打开文件后,使用 defer file.Close()
  2. 正确响应取消信号:在 goroutine 的主循环中,定期检查 ctx.Done() 通道是否关闭。如果关闭,则立即返回,停止 goroutine 的执行。
  3. 传递 Context:在启动子 goroutine 时,将父 goroutine 的 Context 传递给子 goroutine,确保子 goroutine 能收到取消信号。

示例代码

package main

import (
    "context"
    "fmt"
    "io/ioutil"
    "os"
    "time"
)

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    go readFile(ctx)

    time.Sleep(3 * time.Second)
}

func readFile(ctx context.Context) {
    file, err := os.Open("test.txt")
    if err != nil {
        fmt.Println("Error opening file:", err)
        return
    }
    defer file.Close()

    data := make([]byte, 1024)
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Context cancelled, exiting goroutine")
            return
        default:
            n, err := file.Read(data)
            if err != nil {
                fmt.Println("Error reading file:", err)
                return
            }
            fmt.Println("Read", n, "bytes from file")
        }
    }
}

在上述代码中:

  • context.WithTimeout 创建了一个带有超时的 Context,2 秒后自动取消。
  • readFile 函数打开文件,并在循环中读取文件内容。
  • 使用 select 语句监听 ctx.Done() 通道,当 Context 取消时,立即返回,确保 goroutine 正确结束,并通过 defer file.Close() 确保文件资源正确释放。