MST

星途 面试题库

面试题:Go通道与协程的复杂同步问题

假设有多个生产者协程向一个通道发送任务,同时有多个消费者协程从该通道接收任务进行处理。请设计一个Go程序实现这个场景,并处理好生产者和消费者的同步,避免数据竞争和死锁。描述你在设计中采取了哪些措施来确保程序的正确性和高效性。
12.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

// 定义任务结构体
type Task struct {
    ID int
}

func producer(id int, tasks chan<- Task, wg *sync.WaitGroup) {
    defer wg.Done()
    for i := 0; i < 5; i++ {
        task := Task{ID: id*10 + i}
        tasks <- task
        fmt.Printf("Producer %d sent task %d\n", id, task.ID)
    }
}

func consumer(id int, tasks <-chan Task, wg *sync.WaitGroup) {
    defer wg.Done()
    for task := range tasks {
        fmt.Printf("Consumer %d received task %d\n", id, task.ID)
    }
}

func main() {
    const numProducers = 3
    const numConsumers = 2
    tasks := make(chan Task)
    var wg sync.WaitGroup

    // 启动生产者
    for i := 0; i < numProducers; i++ {
        wg.Add(1)
        go producer(i, tasks, &wg)
    }

    // 启动消费者
    for i := 0; i < numConsumers; i++ {
        wg.Add(1)
        go consumer(i, tasks, &wg)
    }

    // 等待所有生产者完成任务
    go func() {
        wg.Wait()
        close(tasks)
    }()

    // 等待所有消费者完成任务
    wg.Wait()
}

确保程序正确性和高效性的措施:

  1. 使用通道进行通信:通过Go语言的通道(channel)来传递任务,这是Go语言中实现并发安全通信的机制,避免了数据竞争。
  2. 同步等待组:使用sync.WaitGroup来等待所有生产者和消费者完成任务。在启动每个生产者和消费者协程时调用wg.Add(1),在协程结束时调用wg.Done(),并在主函数中调用wg.Wait()来等待所有协程完成。
  3. 正确关闭通道:在所有生产者完成任务后,通过close(tasks)关闭通道,这样消费者协程在读取完通道中的所有数据后会退出循环,避免死锁。
  4. 合理分配任务:通过多个生产者和消费者并行工作,提高任务处理的效率。生产者快速地将任务发送到通道,消费者从通道中取出任务并处理,实现了生产者和消费者之间的解耦,提高了程序的整体效率。