MST

星途 面试题库

面试题:Go中Goroutine并发安全与Context取消机制

在一个复杂的Go程序中,多个Goroutine之间存在依赖关系,并且需要根据外部信号(如HTTP请求取消、程序关闭信号等)安全地取消相关Goroutine的执行,以避免资源泄漏和数据不一致问题。请详细描述如何结合`context.Context`机制来实现Goroutine的安全取消,同时要考虑到多个Goroutine之间共享资源的并发安全处理,给出一个完整的示例代码框架,并解释关键部分的实现逻辑。
32.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

结合context.Context实现Goroutine安全取消及并发安全处理

  1. context.Context机制概述context.Context是Go语言中用于控制Goroutine生命周期的重要机制。它可以在Goroutine树中传递截止时间、取消信号等。context.Context有几个重要的接口方法:

    • Deadline():返回截止时间,用于控制Goroutine在某个时间点后自动取消。
    • Done():返回一个通道,当context被取消或超时时,该通道会被关闭。
    • Err():返回context被取消的原因。
    • Value(key interface{}) interface{}:用于在context中传递一些附加数据。
  2. 并发安全处理共享资源: 为了处理多个Goroutine之间共享资源的并发安全问题,通常使用sync.Mutex(互斥锁)、sync.RWMutex(读写互斥锁,适用于读多写少场景)或sync.Cond(条件变量)等同步原语。

  3. 示例代码框架

package main

import (
    "context"
    "fmt"
    "sync"
    "time"
)

// 模拟共享资源
type SharedResource struct {
    data int
    mu   sync.Mutex
}

// 向共享资源写入数据
func (sr *SharedResource) Write(ctx context.Context, value int) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Write operation cancelled")
            return
        default:
            sr.mu.Lock()
            sr.data = value
            fmt.Printf("Write value: %d\n", value)
            sr.mu.Unlock()
            time.Sleep(100 * time.Millisecond)
        }
    }
}

// 从共享资源读取数据
func (sr *SharedResource) Read(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Read operation cancelled")
            return
        default:
            sr.mu.Lock()
            fmt.Printf("Read value: %d\n", sr.data)
            sr.mu.Unlock()
            time.Sleep(100 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    sharedResource := &SharedResource{}

    var wg sync.WaitGroup
    wg.Add(2)

    // 启动写操作的Goroutine
    go func() {
        defer wg.Done()
        sharedResource.Write(ctx, 42)
    }()

    // 启动读操作的Goroutine
    go func() {
        defer wg.Done()
        sharedResource.Read(ctx)
    }()

    // 模拟外部信号,500毫秒后取消
    time.Sleep(500 * time.Millisecond)
    cancel()

    wg.Wait()
    fmt.Println("All Goroutines stopped")
}
  1. 关键部分实现逻辑
    • context.Context创建与取消: 在main函数中,使用context.WithCancel(context.Background())创建了一个可取消的context,并返回一个取消函数cancel。当外部信号(这里模拟为time.Sleep(500 * time.Millisecond)后)到来时,调用cancel()函数,向所有基于该context创建的子context发送取消信号。
    • SharedResource中的操作WriteRead方法中,通过select语句监听ctx.Done()通道。当该通道关闭时,意味着context被取消,Goroutine会退出循环,从而安全地结束执行。同时,在对共享资源data进行读写操作时,使用sync.Mutex来保证并发安全。
    • sync.WaitGroup的使用sync.WaitGroup用于等待所有Goroutine完成。在启动每个Goroutine前调用wg.Add(1),在Goroutine结束时调用wg.Done(),最后通过wg.Wait()阻塞主线程,直到所有Goroutine完成任务。这样可以确保在程序退出前,所有Goroutine都能安全地结束,避免资源泄漏。