使用go-future替代sync.WaitGroup简化并发编程

ycl2018 · · 63 次点击 · 开始浏览    置顶

go语言中经常使用`sync.WaitGroup`并发执行多个任务,然后使用Wait等待任务执行完成。实际开发中,经常需要在多个任务中调用接口,需要获取到接口返回值和错误,并在外部函数中处理。这样就需要先声明返回值和错误,用起来不太方便。推荐一个简单的三方库`go-future`,支持范型,可以方便的替代WaitGroup,简化并发开发体验: 获取: ```shell go get github.com/ycl2018/go-future (go >=1.20) ``` ##### 使用`sync.WaitGroup`开发对比使用`go-future` 使用`sync.WaitGroup` ```go func TestWaitGroup2(t *testing.T) { var swg sync.WaitGroup swg.Add(2) // 需要先声明返回值和错误 var ret1, ret2 string var err1, err2 error go func() { defer func() { // 处理panic if e := recover(); e != nil { t.Log(e) } }() defer swg.Done() ret1 = "bar" err1 = nil }() go func() { defer func() { if e := recover(); e != nil { t.Log(e) } }() defer swg.Done() ret2 = "bar" err2 = errors.New("ops") }() // 等待任务返回 swg.Wait() t.Log(ret1, ret2, err1, err2) } ``` 使用`go-future`可以大大简化开发体验 ```go func TestFuture(t *testing.T) { // 启动异步future任务 f1 := future.Go(func() (string, error) { return "foo", nil }) f2 := future.Go(func() (string, error) { return "bar", nil }) // 收集结果,自动推算类型无需转换 foo, bar, err := future.Collect(f1, f2) t.Logf("foo:%s bar:%s err:%v", foo, bar, err) } ``` ##### `go-future`的更多功能: - [x] 支持范型,根据任务类型返回对应类型的Future,无需类型转换 - [x] 支持多返回值类型任务:从单返回值到至多5个返回值+error - [x] 支持重复从future中`Wait`获取结果,并发高效、安全 - [x] 支持`Collect`多个Future任务,等待完成并收集结果和错误,8个值内提供范型支持 - [x] 支持`Then`在Future任务完成且无错误返回时执行后置任务 - [x] 支持`Handle`在Future任务完成时检查错误并执行后置处理 - [x] 支持`Check`在链路节点完成时进行错误处理/结果检查 - [x] 支持链式`Join`其他Future任务 - [x] 支持设置超时时间 #### 综合使用demo ```go // 做番茄炒鸡蛋&番茄牛腩 func TestFuture2(t *testing.T) { // 洗菜然后切菜 washAndCut := future.Go(func() ([]string, error) { var ret = []string{"西红柿", "牛腩"} log.Printf("洗好了%v", ret) return ret, nil }).Then(func(washed []string) (any, error) { log.Printf("切好了%v", washed) return washed, nil }) // 打几个鸡蛋 hitEggs := future.Go(func() (string, error) { log.Printf("打好了鸡蛋") return "鸡蛋", nil }) // 做番茄炒蛋 doTomatoEggs := washAndCut. Join(hitEggs). Then(func(vegetable any, eggs any) (any, error) { v := vegetable.([]string) return fmt.Sprintf("炒好的[%s%s]", v[0], eggs), nil }) // 做番茄牛腩 doTomatoBeef := washAndCut.Then(func(vegetable any) (any, error) { v := vegetable.([]string) return fmt.Sprintf("炒好的[%s%s]", v[0], v[1]), nil }) // 等待所有任务完成 v0, v1, _ := future.Collect(doTomatoBeef, doTomatoEggs) t.Logf("最终获得了:%s,%s", v0, v1) //2024/11/14 14:15:09 打好了鸡蛋 //2024/11/14 14:15:09 洗好了[西红柿 牛腩] //2024/11/14 14:15:09 切好了[西红柿 牛腩] // future_test.go:34: 最终获得了:炒好的[西红柿牛腩],炒好的[西红柿鸡蛋] //--- PASS: TestFuture (0.00s) //PASS } ``` ### 项目地址:[开源地址](https://github.com/ycl2018/go-future) **觉得好用记得给作者点个🌟🌟~**

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

63 次点击  ∙  1 赞  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传