MST

星途 面试题库

面试题:Go语言Goroutine与Channel的复杂同步问题

假设有多个Goroutine并发执行任务,每个任务执行完毕后需要向主Goroutine发送信号,告知任务已完成。主Goroutine在接收到所有任务完成的信号后,汇总结果并打印。请使用Go语言的Goroutine和Channel实现这个逻辑,并阐述在实现过程中如何避免死锁,以及如何处理可能出现的竞争条件。
28.9万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
)

func worker(id int, resultChan chan int, doneChan chan struct{}) {
    // 模拟任务执行
    var res int
    for i := 0; i < 10; i++ {
        res += i
    }
    resultChan <- res
    doneChan <- struct{}{}
}

func main() {
    numWorkers := 5
    resultChan := make(chan int)
    doneChan := make(chan struct{}, numWorkers)

    for i := 0; i < numWorkers; i++ {
        go worker(i, resultChan, doneChan)
    }

    var totalResult int
    for i := 0; i < numWorkers; i++ {
        totalResult += <-resultChan
        <-doneChan
    }

    close(resultChan)
    close(doneChan)

    fmt.Printf("汇总结果: %d\n", totalResult)
}

避免死锁

  1. 缓冲区设置:在创建doneChan时,设置了缓冲区大小为numWorkers。这样可以避免在worker还未向doneChan发送数据时,main函数就开始接收,从而导致死锁。
  2. 接收与关闭顺序:在main函数中,先接收resultChan中的结果,再接收doneChan中的信号,确保每个worker都有机会发送完成信号。同时,在所有数据接收完毕后,关闭resultChandoneChan,防止向已关闭的通道发送数据造成死锁。

处理竞争条件

  1. 通道同步:通过使用通道(resultChandoneChan)来进行数据传递和同步,Goroutine之间的通信是线程安全的,避免了传统共享内存带来的竞争条件。每个worker将结果发送到resultChan,主GoroutineresultChan接收,这一过程是有序且安全的。
  2. 无共享数据:在worker函数中,每个worker都有自己独立的res变量,不存在多个Goroutine同时读写同一变量的情况,进一步避免了竞争条件。