MST

星途 面试题库

面试题:Go语言中如何优雅地处理多个Goroutine通过Channel通信时的资源竞争和死锁问题

假设存在多个Goroutine通过Channel进行数据传递和同步操作,在这个过程中可能会出现资源竞争和死锁情况。请描述你会采取哪些策略和方法来避免这些问题,并且给出一个简单的代码示例来展示如何通过合理的Channel使用与同步机制来确保程序的正确性和稳定性。
46.8万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试

避免资源竞争和死锁的策略

  1. 合理使用Channel
    • 明确Channel的方向,使用带方向的Channel(如chan<- 发送方向,<-chan 接收方向),这样在编译期就能检查出一些非法操作,比如在接收方向的Channel上进行发送操作。
    • 确保发送和接收操作匹配,避免出现发送数据但无人接收,或者接收数据但无人发送的情况。
  2. 同步机制
    • 使用sync.WaitGroup来等待所有Goroutine完成任务。它可以用来阻塞当前Goroutine,直到所有被等待的Goroutine都调用了Done方法。
    • 使用sync.Mutex 对共享资源进行保护,在访问共享资源前加锁,访问完成后解锁,防止多个Goroutine同时访问共享资源导致数据竞争。
  3. 控制Goroutine数量:避免创建过多的Goroutine导致系统资源耗尽,同时也能减少死锁的可能性。可以使用worker pool模式来限制同时运行的Goroutine数量。

代码示例

package main

import (
    "fmt"
    "sync"
)

func worker(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
    defer wg.Done()
    for j := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, j)
        result := j * 2
        fmt.Printf("Worker %d finished job %d, result: %d\n", id, j, result)
        results <- result
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)
    var wg sync.WaitGroup

    // 创建3个worker
    for w := 1; w <= 3; w++ {
        wg.Add(1)
        go worker(w, jobs, results, &wg)
    }

    // 发送任务
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    // 等待所有任务完成
    go func() {
        wg.Wait()
        close(results)
    }()

    // 接收结果
    for r := range results {
        fmt.Printf("Result: %d\n", r)
    }
}

在这个示例中:

  • worker函数从jobs通道接收任务,处理后将结果发送到results通道,并使用WaitGroup标记任务完成。
  • main函数创建了3个worker,向jobs通道发送任务,然后等待所有worker完成任务并关闭results通道,最后从results通道接收结果。通过这种方式,既避免了资源竞争,也防止了死锁。