凑篇幅的introduction
这篇文章希望可以跟大家分享一下写go的一些心得
写这个文章主要的原因是Q1 OKR还没完成,手里目前又没有很深度的技术原理干货。只能将就着分享一下平时写代码的经验了(理由来自Kraken)
正文
goroutine的并发数和子任务超时时间控制
在日常操作中,有大量如此操作:枚举一个id数组,并行对每个id做某种增删改除。为了避免因为某个id的不稳定操作(连接数据库,网络异常等)导致的整体效率下降,最常见的写法就是每个元素开一个独立的goroutine去处理。但是这种做法容易造成超高的并发导致拥堵,最终降低了反而降低了整体效率。
所以我写了一个库用来控制goroutine的并发数和子任务的超时时间, 这个库的来源是之前用过的一个node库promise-limit。使用Map或者更好一点的MapWithTimeout,可以控制最高并发数量workNums,和每个子任务允许的操作时间timeout。一个具体例子长这样:
Ids := []int{1, 2, 3}
items := make([]*Item, len(Ids))
err := routine.MapWithTimeout(len(items), 5, time.Second, func(ctx context.Context, i int) {
item := getItemByID(Ids[i])
if ctx.Err() == nil {
items[i] = item
}
})
具体的原理并不麻烦,就是通过固定数量的worker拿到通道中的数据进行操作,具体实现可以看代码https://github.com/leoython/r...
稍微做点性能优化
细心的同学可以从代码中看到,我并没有使用go原生的mutex,而是自己写一个简单的spin lock.
mutex 需要把等待锁的 goroutine 放置在等待队列,等到锁释放了才唤醒,使用 spin-lock 是利用 gosched 抢占调度主动让出 CPU 并且把当前 goroutine 保存堆栈状态,
另外,其实 mutex 内部也实现了 spin-lock,但是这个内置的 spin-lock 机制只会 spin 几次而已,而且还有其他的限制条件,有兴趣可看下源码:https://github.com/golang/go/blob/97d0505334c71a8d7a1e7431c1e1515c93b59e2b/src/runtime/proc.go#L5321-L5334
有疑问加站长微信联系(非本文作者)