MST

星途 面试题库

面试题:Go接口响应式编程之并发与接口结合

假设你有一个`Worker`接口,包含`DoWork`方法。实现多个结构体满足该接口,每个结构体的`DoWork`方法执行不同的耗时任务(比如模拟网络请求或文件读取)。使用Go的并发特性,创建多个`Worker`实例并并发执行它们的`DoWork`方法,最后收集所有结果并返回。要求处理可能出现的错误,并且合理使用`context`来控制并发任务的生命周期。
20.4万 热度难度
编程语言Go

知识考点

AI 面试

面试题答案

一键面试
package main

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

// Worker接口
type Worker interface {
    DoWork(ctx context.Context) (string, error)
}

// 结构体1
type Worker1 struct{}

func (w *Worker1) DoWork(ctx context.Context) (string, error) {
    select {
    case <-time.After(2 * time.Second):
        return "Worker1完成任务", nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

// 结构体2
type Worker2 struct{}

func (w *Worker2) DoWork(ctx context.Context) (string, error) {
    select {
    case <-time.After(3 * time.Second):
        return "Worker2完成任务", nil
    case <-ctx.Done():
        return "", ctx.Err()
    }
}

// 并发执行所有Worker的DoWork方法并收集结果
func ExecuteWorkers(ctx context.Context, workers []Worker) ([]string, error) {
    var results []string
    var errCount int
    var totalWorkers = len(workers)
    semaphore := make(chan struct{}, 5) // 限制并发数量

    var done = make(chan struct{})
    defer close(done)

    for _, worker := range workers {
        semaphore <- struct{}{}
        go func(w Worker) {
            defer func() { <-semaphore }()
            result, err := w.DoWork(ctx)
            if err != nil {
                errCount++
            } else {
                results = append(results, result)
            }
            if errCount+len(results) == totalWorkers {
                close(done)
            }
        }(worker)
    }

    <-done

    if errCount > 0 {
        return nil, fmt.Errorf("%d 个任务出现错误", errCount)
    }
    return results, nil
}

你可以使用以下方式调用:

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()

    workers := []Worker{
        &Worker1{},
        &Worker2{},
    }

    results, err := ExecuteWorkers(ctx, workers)
    if err != nil {
        fmt.Println("执行任务出错:", err)
    } else {
        fmt.Println("所有任务结果:", results)
    }
}
  1. 定义了Worker接口,包含DoWork方法。
  2. 创建了两个实现Worker接口的结构体Worker1Worker2,其DoWork方法模拟了不同的耗时任务,并通过select语句结合context来处理任务取消。
  3. ExecuteWorkers函数并发执行所有WorkerDoWork方法,并收集结果。这里使用了信号量(semaphore)来限制并发数量,同时通过done通道来等待所有任务完成,并处理可能出现的错误。
  4. main函数中创建context并设置超时,实例化Worker并调用ExecuteWorkers函数执行任务。