设计思路
- 创建根上下文:使用
context.WithTimeout
创建带有总超时时间的根上下文,这将作为整个请求的上下文,控制所有子任务的执行时间。
- 任务依赖处理:对于有依赖关系的任务,将前序任务的结果传递给后续任务。例如,任务A完成后,将其结果作为参数传递给任务B和任务C。
- 子任务并发执行:利用
sync.WaitGroup
来等待所有子任务完成。每个子任务在一个单独的goroutine中执行,并且将根上下文或基于根上下文衍生的子上下文传递给每个子任务。
- 错误处理与取消:当某个子任务失败时,通过上下文的取消机制通知其他相关任务取消执行。每个子任务在执行过程中需要定期检查上下文的取消信号。
关键代码片段
package main
import (
"context"
"fmt"
"sync"
"time"
)
// 模拟任务A
func taskA(ctx context.Context) (string, error) {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(2 * time.Second):
return "result of A", nil
}
}
// 模拟任务B,依赖任务A的结果
func taskB(ctx context.Context, aResult string) (string, error) {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(2 * time.Second):
return "result of B based on " + aResult, nil
}
}
// 模拟任务C,依赖任务A的结果
func taskC(ctx context.Context, aResult string) (string, error) {
select {
case <-ctx.Done():
return "", ctx.Err()
case <-time.After(2 * time.Second):
return "result of C based on " + aResult, nil
}
}
func main() {
var wg sync.WaitGroup
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// 执行任务A
var aResult string
var aErr error
wg.Add(1)
go func() {
defer wg.Done()
aResult, aErr = taskA(ctx)
}()
// 等待任务A完成
wg.Wait()
if aErr != nil {
fmt.Println("task A failed:", aErr)
return
}
// 执行任务B和C
var bResult string
var bErr error
var cResult string
var cErr error
wg.Add(2)
ctxB, cancelB := context.WithCancel(ctx)
ctxC, cancelC := context.WithCancel(ctx)
defer cancelB()
defer cancelC()
go func() {
defer wg.Done()
bResult, bErr = taskB(ctxB, aResult)
}()
go func() {
defer wg.Done()
cResult, cErr = taskC(ctxC, aResult)
}()
// 等待任务B和C完成
wg.Wait()
if bErr != nil {
fmt.Println("task B failed:", bErr)
cancelC()
}
if cErr != nil {
fmt.Println("task C failed:", cErr)
cancelB()
}
fmt.Println("task A result:", aResult)
fmt.Println("task B result:", bResult)
fmt.Println("task C result:", cResult)
}
代码说明
- 任务函数:
taskA
、taskB
和 taskC
分别模拟不同的任务,taskB
和 taskC
依赖 taskA
的结果。
- 主函数:
- 创建带有超时的根上下文
ctx
和取消函数 cancel
。
- 并发执行
taskA
,并等待其完成。
- 根据
taskA
的结果,并发执行 taskB
和 taskC
,同时为它们创建可取消的子上下文。
- 等待
taskB
和 taskC
完成,若其中一个失败,则取消另一个任务。
- 最后输出各个任务的结果或错误信息。