面试题答案
一键面试package main
import (
"context"
"fmt"
"time"
)
// Task 定义任务类型
type Task struct {
Name string
Timeout time.Duration
Exec func(ctx context.Context) error
ErrorType string
}
// TaskResult 定义任务结果类型
type TaskResult struct {
TaskName string
Error error
ErrorType string
}
// RunTasks 并发运行任务
func RunTasks(tasks []Task) ([]TaskResult, error) {
var results []TaskResult
var overallErr error
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
resultChan := make(chan TaskResult, len(tasks))
for _, task := range tasks {
go func(t Task) {
innerCtx, innerCancel := context.WithTimeout(ctx, t.Timeout)
defer innerCancel()
err := t.Exec(innerCtx)
result := TaskResult{
TaskName: t.Name,
Error: err,
ErrorType: t.ErrorType,
}
resultChan <- result
}(task)
}
for i := 0; i < len(tasks); i++ {
result := <-resultChan
results = append(results, result)
if result.Error != nil {
overallErr = fmt.Errorf("%w; task %s failed with error: %v", overallErr, result.TaskName, result.Error)
}
}
close(resultChan)
return results, overallErr
}
// 示例数据库查询任务
func dbQuery(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 模拟数据库查询
time.Sleep(2 * time.Second)
return nil
}
}
// 示例文件读取任务
func fileRead(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 模拟文件读取
time.Sleep(3 * time.Second)
return nil
}
}
// 示例网络请求任务
func networkRequest(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
// 模拟网络请求
time.Sleep(4 * time.Second)
return nil
}
}
实现思路
- 定义任务和结果类型:
Task
结构体定义每个任务的属性,包括任务名称Name
、超时时间Timeout
、执行函数Exec
以及错误类型ErrorType
。TaskResult
结构体定义任务执行后的结果,包括任务名称TaskName
、可能出现的错误Error
以及错误类型ErrorType
。
- 并发运行任务:
RunTasks
函数负责并发运行所有任务。它使用context.WithCancel
创建一个上下文,以便在任何任务出错时能够取消所有其他正在运行的任务。- 对于每个任务,使用
context.WithTimeout
创建具有特定超时时间的子上下文,并在 goroutine 中执行任务。 - 每个任务执行完成后,将结果发送到
resultChan
通道。
- 收集结果和处理错误:
- 主程序从
resultChan
通道收集所有任务的结果,并统计出现的错误。如果有任何任务出错,RunTasks
函数返回的overallErr
会包含错误信息。
- 主程序从
- 示例任务函数:
dbQuery
、fileRead
和networkRequest
是示例任务函数,它们模拟实际的数据库查询、文件读取和网络请求操作,并通过select
语句来处理上下文的取消信号,以实现超时控制。
使用示例
func main() {
tasks := []Task{
{
Name: "DB Query",
Timeout: 3 * time.Second,
Exec: dbQuery,
ErrorType: "Database Error",
},
{
Name: "File Read",
Timeout: 2 * time.Second,
Exec: fileRead,
ErrorType: "File Error",
},
{
Name: "Network Request",
Timeout: 5 * time.Second,
Exec: networkRequest,
ErrorType: "Network Error",
},
}
results, err := RunTasks(tasks)
if err != nil {
fmt.Println("Overall error:", err)
}
for _, result := range results {
if result.Error != nil {
fmt.Printf("Task %s failed with error type %s: %v\n", result.TaskName, result.ErrorType, result.Error)
} else {
fmt.Printf("Task %s succeeded\n", result.TaskName)
}
}
}
在 main
函数中,创建了三个不同类型的任务,并设置了各自的超时时间。通过调用 RunTasks
函数并发执行这些任务,并处理任务执行结果和错误。这样就实现了一个通用的框架来处理高并发任务的并发执行、超时控制以及错误处理。