本文介绍一种在 Go 语言中进行更优雅的错误处理方法,尤其适用于并发任务场景。核心思想是在现有上下文基础上构建一个包装器,以便集中管理和处理任务执行过程中的错误。
挑战与解决方案
并发任务处理常常面临以下挑战:
- 多个 goroutine 的错误收集
- 线程安全性的维护
- 并发执行的限制
- 同时保留首个错误和所有错误
- 简洁高效的错误处理机制
为此,我们构建了一个 taskcontext 来应对这些挑战:
package taskctx import ( "context" "errors" "fmt" "sync" ) type runfn[T any] func() (T, error) type taskcontext struct { context.Context mu sync.RWMutex err error multierr []error } func newtaskcontext(parent context.Context) *taskcontext { if parent == nil { panic("cannot create context from nil parent") } return &taskcontext{Context: parent} }
登录后复制
关键特性
- 线程安全错误处理:
func (c *taskcontext) witherror(err error) *taskcontext { if err == nil { return c } c.mu.Lock() defer c.mu.Unlock() c.multierr = append(c.multierr, err) if c.err == nil { c.err = err } else { c.err = errors.Join(c.err, err) } return c }
登录后复制
- 单任务执行:
func run[T any](ctx *taskcontext, fn runfn[T]) T { var zero T if err := ctx.Err(); err != nil { return zero } result, err := fn() if err != nil { ctx.witherror(err) return zero } return result }
登录后复制
- 并行任务执行:
func runparallel[T any](ctx *taskcontext, fns ...func() (T, error)) ([]T, error) { if err := ctx.Err(); err != nil { return nil, err } results := make([]T, len(fns)) var resultsmu sync.Mutex var wg sync.WaitGroup wg.Add(len(fns)) for i, fn := range fns { i, fn := i, fn go func() { defer wg.Done() result, err := fn() if err != nil { ctx.witherror(fmt.Errorf("task %d: %w", i+1, err)) } else { resultsmu.Lock() results[i] = result resultsmu.Unlock() } }() } wg.Wait() return results, ctx.errors() }
登录后复制
- 受限并发:
func runparallelwithlimit[T any](ctx *taskcontext, limit int, fns ...func() (T, error)) ([]T, error) { // ... 使用信号量实现受限并发 ... }
登录后复制
使用示例
- 单任务执行示例:
// ... (示例代码略,与原文类似) ...
登录后复制
登录后复制
- 并行任务执行示例:
// ... (示例代码略,与原文类似) ...
登录后复制
登录后复制
优势
- 线程安全:使用互斥锁保护共享资源。
- 错误收集:同时记录首个错误和所有错误。
- 上下文集成:与 Go 的 context 包无缝集成。
- 通用性:支持任意返回类型。
- 并发控制:内置支持并发限制。
测试
// ... (测试代码略,与原文类似) ...
登录后复制
总结
taskcontext 提供了一种在 Go 中高效处理并发任务执行和错误管理的方案,尤其适用于需要同时执行多个任务、收集所有错误、限制并发度以及保证线程安全的场景。 完整代码可在 Github 上获取。(此处需补充Github链接,如果原文提供的话)
讨论
您在 Go 中是如何处理并发任务执行和错误处理的?欢迎在评论区分享您的经验和想法!
(联系方式与原文保持一致)
以上就是在 Go 中构建健壮的任务执行上下文的详细内容,更多请关注php中文网其它相关文章!