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

ycl2018 · 3月之前 · 324 次点击 · 大约8小时之前 开始浏览    置顶

go语言中经常使用sync.WaitGroup并发执行多个任务,然后使用Wait等待任务执行完成。实际开发中,经常需要在多个任务中调用接口,需要获取到接口返回值和错误,并在外部函数中处理。这样就需要先声明返回值和错误,用起来不太方便。推荐一个简单的三方库go-future,支持范型,可以方便的替代WaitGroup,简化并发开发体验:

获取:

go get github.com/ycl2018/go-future  (go >=1.20)
使用sync.WaitGroup开发对比使用go-future

使用sync.WaitGroup

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可以大大简化开发体验

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

// 做番茄炒鸡蛋&番茄牛腩
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
}

项目地址:开源地址

觉得好用记得给作者点个🌟🌟~


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

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

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