面试题答案
一键面试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)
}
}
- 定义了
Worker
接口,包含DoWork
方法。 - 创建了两个实现
Worker
接口的结构体Worker1
和Worker2
,其DoWork
方法模拟了不同的耗时任务,并通过select
语句结合context
来处理任务取消。 ExecuteWorkers
函数并发执行所有Worker
的DoWork
方法,并收集结果。这里使用了信号量(semaphore
)来限制并发数量,同时通过done
通道来等待所有任务完成,并处理可能出现的错误。- 在
main
函数中创建context
并设置超时,实例化Worker
并调用ExecuteWorkers
函数执行任务。