MST

星途 面试题库

面试题:Go for语句在并发场景下的策略运用

假设你需要使用Go语言编写一个程序,通过多个goroutine并发处理任务。每个goroutine会从一个共享的任务队列(可以用切片模拟)中取出任务并处理。要求使用for语句来管理goroutine的任务获取和处理逻辑,同时要处理好并发安全问题(如避免竞态条件)。请编写完整代码,并详细说明你在for循环中采用的策略,以及如何保证并发环境下程序的正确性和高效性。
26.3万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

import (
    "fmt"
    "sync"
)

// Task 代表一个任务
type Task struct {
    ID int
}

func main() {
    // 模拟任务队列
    tasks := []Task{
        {ID: 1},
        {ID: 2},
        {ID: 3},
        {ID: 4},
        {ID: 5},
    }

    var wg sync.WaitGroup
    var mu sync.Mutex
    taskIndex := 0

    // 启动3个goroutine来处理任务
    numGoroutines := 3
    for i := 0; i < numGoroutines; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for {
                mu.Lock()
                if taskIndex >= len(tasks) {
                    mu.Unlock()
                    break
                }
                task := tasks[taskIndex]
                taskIndex++
                mu.Unlock()

                // 处理任务
                processTask(task)
            }
        }()
    }

    wg.Wait()
}

// processTask 处理单个任务
func processTask(task Task) {
    fmt.Printf("Processing task %d\n", task.ID)
}

for循环中的策略

  1. 任务获取:在for循环内部,首先使用互斥锁mu来保护对共享任务队列索引taskIndex的访问。每次获取任务前,锁定互斥锁,检查taskIndex是否超过任务队列的长度。如果没有超过,则取出任务,并将taskIndex递增,然后解锁互斥锁。
  2. 任务处理:获取任务后,在临界区(互斥锁保护的区域)之外处理任务,这样可以在处理任务时不占用锁,提高并发效率。

保证程序正确性和高效性的方式

  1. 正确性
    • 互斥锁:通过sync.Mutex来确保同一时间只有一个goroutine可以访问和修改共享的任务队列索引taskIndex,从而避免竞态条件。
    • 边界检查:在每次获取任务前,检查taskIndex是否超过任务队列的长度,确保不会越界访问。
  2. 高效性
    • 减少锁的持有时间:只在获取任务时持有锁,任务处理在锁之外进行,这样可以让其他goroutine有更多机会获取任务,提高并发度。
    • 合理设置goroutine数量:根据实际情况(如CPU核心数、任务类型等)合理设置goroutine的数量,避免过多或过少的goroutine导致资源浪费或性能瓶颈。例如在上述代码中设置了3个goroutine来处理任务。