当前位置: 首页 > 科技观察

说说并发库Conc,你学会了吗?

时间:2023-03-19 12:54:07 科技观察

上个月,sourcegraph发布了conc[1]并发库。目标是为go实现更好的结构化并发。我想简单评论一下,每个公司都有类似的轮子。和之前的库相比,泛型更多,代码写起来更优雅,不需要接口,不需要runtimeassert,性能肯定更好。我们在写通用库和框架的时候,都有一个原则,就是并发控制和业务逻辑分离。如果背离了这个原则,我们肯定做不出一个通用的库。总体介绍1.WaitGroup和Panic标准库自带sync.WaitGroup等待goroutine运行完毕。缺点是我们在代码的控制部分要处理大量的wg.Add和wg.Done函数,所以一般封装到右边的库类型WaitGroupstruct{wgsync.WaitGrouppcpanics.Catcher}//Go在WaitGroup.func(h*WaitGroup)Go(ffunc()){h.wg.Add(1)gofunc(){deferh.wg.Done()h.pc.Try(f)}()}但是如何处理恐慌呢?只需在闭包doSomething运行时添加一个safeGo函数,以捕获recovernativeGo产生的大量无用代码。我们的repo已经很运动地清理了,也遇到过goroutine忘记写recover导致的意外。conc还提供了一个捕获器来封装恢复逻辑。conc.WaitGroup可以选择Wait来重新抛出恐慌,或者WaitAndRecover可以返回捕获的恐慌堆栈信息h.pc.Repanic()}func(h*WaitGroup)WaitAndRecover()*panics.RecoveredPanic{h.wg.Wait()//如果我们从子goroutine中捕获到恐慌,则返回恢复的恐慌。returnh.pc.Recovered()}2.ForEach和Map有很多高级语言的基础操作,在go里很奢侈,只能写很多繁琐的代码。conc封装了iterator和mapperfunc的通用版本Map(input,f)}以上为使用示例,用户只需编写业务函数句柄即可。与go1.19之前的版本相比,泛型的引入使得基础库的编写更加简单//Iterator也可以安全复用和并发使用typeIterator[Tany]struct{//MaxGoroutines控制最大goroutines个数//用于此迭代器的方法。////如果不设置,MaxGoroutines默认为runtime.GOMAXPROCS(0).MaxGoroutinesint}MaxGoroutines默认GOMAXPROCS并发处理参数切片,也可以自定义。我个人认为这是不合理的。默认值为1。//ForEachIdx与ForEach相同,只是它还提供//元素的索引给回调。funcForEachIdx[Tany](input[]T,ffunc(int,*T)){Iterator[T]{}.ForEachIdx(input,f)}ForEachIdx在创建Iterator[T]{}时可以自定义并发度,最后调用iter。ForEachIdx//ForEachIdx与ForEach相同,只是它还提供//元素的索引给回调函数。func(iterIterator[T])ForEachIdx(input[]T,ffunc(int,*T)){......varidxatomic.Int64//在循环外创建任务以避免额外的闭包分配。task:=func(){i:=int(idx.Add(1)-1)for;我