Go使用协程批量获取数据加快接口返回速度
作者:HappyTeemo 发布时间:2024-05-21 10:23:38
推荐go学习书籍,点击链接跳转京东官方商城购买。
服务端经常需要返回一个列表,里面包含很多用户数据,常规做法当然是遍历然后读缓存。
使用Go语言后,可以并发获取,极大提升效率。
使用channel
Copy Highlighter-hljs
package main
import (
"fmt"
"time"
)
func add2(a, b int, ch chan int) {
c := a + b
fmt.Printf("%d + %d = %d\n", a, b, c)
ch <- 1 //执行完了就写一条表示自己完成了
}
func main() {
start := time.Now()
chs := make([]chan int, 10)
for i := 0; i < 10; i++ {
chs[i] = make(chan int)
go add2(1, i, chs[i]) //分配了10个协程出去了
}
for _, ch := range chs {
<-ch //循环等待,要每个完成才能继续,不然就等待
}
end := time.Now()
consume := end.Sub(start).Seconds()
fmt.Println("程序执行耗时(s):", consume)
}
在每个协程的 add() 函数业务逻辑完成后,我们通过 ch <- 1 语句向对应的通道中发送一个数据。
在所有的协程启动完成后,我们再通过 <-ch 语句从通道切片 chs 中依次接收数据(不对结果做任何处理,相当于写入通道的数据只是个标识而已,表示这个通道所属的协程逻辑执行完毕).
直到所有通道数据接收完毕,然后打印主程序耗时并退出。
使用WaitGroup
Add:WaitGroup 类型有一个计数器,默认值是0,我们可以通过 Add 方法来增加这个计数器的值,通常我们可以通过个方法来标记需要等待的子协程数量;
Done:当某个子协程执行完毕后,可以通过 Done 方法标记已完成,该方法会将所属 WaitGroup 类型实例计数器值减一,通常可以通过 defer 语句来调用它;
Wait:Wait 方法的作用是阻塞当前协程,直到对应 WaitGroup 类型实例的计数器值归零,如果在该方法被调用的时候,对应计数器的值已经是 0,那么它将不会做任何事情
package main
import (
"fmt"
"sync"
)
func addNum(a, b int, deferFunc func()) {
defer func() {
deferFunc()
}()
c := a + b
fmt.Printf("%d + %d = %d\n", a, b, c)
}
func main() {
var wg sync.WaitGroup
wg.Add(10) //等于发了10个令牌
for i := 0; i < 10; i++ {
go addNum(i, 1, wg.Done) //每次执行都消耗令牌
}
wg.Wait() //等待令牌消耗完
}
需要注意的是,该类型计数器不能小于0,否则会抛出如下 panic:
panic: sync: negative WaitGroup counter
应用到实践
func GetManyBase(userIds []int64) []UserBase {
userCaches := make([]UserBase, len(userIds))
var wg sync.WaitGroup
for index, userId := range userIds {
wg.Add(1)
go func(index int, userId int64, userCaches []UserBase) {
userCaches[index] = NewUserCache(userId).GetBase()
wg.Done()
}(index, userId, userCaches)
}
wg.Wait()
return userCaches
}
这种写法有两个问题:
1.并发肯定带来乱序,所以要考虑需要排序的业务场景。
2.map是线程不安全的,并发读写会panic。
优化一下:
func GetManyBase(userIds []int64) []UserBase {
userCaches := make([]UserBase, len(userIds))
var scene sync.Map
var wg sync.WaitGroup
for index, userId := range userIds {
wg.Add(1)
go func(index int, userId int64, userCaches []UserBase) {
scene.Store(userId, NewUserCache(userId).GetBase())
wg.Done()
}(index, userId, userCaches)
}
wg.Wait()
i := 0
for _, userId := range userIds {
if value, ok := scene.Load(userId); ok {
userCaches[i] = value.(UserBase)
}
i++
}
return userCaches
}
为什么不直接上锁?
因为经过我的测试,会很慢,没有sync.Map优化的好。
这样可以保证顺序。
来源:https://www.cnblogs.com/HappyTeemo/p/17097505.html
猜你喜欢
- 一、数据导入是所有数模编程的第一步编程求解一个数模问题,问题总会涉及一些数据。有些数据是在题目的文字描述中给出的,有些数据是通过题目的附件文
- 回想下,在 Python 中编程时,你是否曾经需要检查某个可迭代对象(如列表)中的任何元素或所有元素的计算结果是否为True?假设,我们要判
- 具体代码如下所示:import numpy as npfrom matplotlib import pyplot as pltfrom sc
- ALTER TABLE将表更改为当前字符集。如果在执行ALTER TABLE操作期间遇到重复键错误,原因在于新的字符集将2个键映射到了相同值
- 问题描述像这样的图,我想把右边的colorbar设置成和主图一样高度方法0. plt.colorbar参数介绍https://matplot
- 前言上位机实战开发先放一放,今天来学习一个新的内容—NumPy的使用1 一维数组例:用普通方法生成一维数组num = [0 for i in
- import matplotlib.pyplot as plt #plt用于显示图片import matplotlib.image as m
- 目录一、scrapy 分析1. 解析函数或数据入库出错,不会重试,会造成一定的数据丢失2. 运行方式,需借助命令行,不方便调试3. 入库 p
- 为了能让PHP连接MSSQL,系统需要安装MSSQL,PHP,且在PHP.ini中的配置中,将 ;extension=
- 会用到的库的1、selenium的webdriver2、tesserocr或者pytesseract进行图像识别3、pillow的Image
- MySQL启用SSD存储的实例详解有时OS读写慢会降低MySQL服务器的性能,尤其是OS与MySQL使用同一磁盘时。故最好是让MySQL使用
- 如下所示:import numpya = numpy.array(([3,2,1],[2,5,7],[4,7,8]))itemindex =
- 一、验证计算机服务列表中是否有MySQL服务(1)右击桌面上的计算机图标出现菜单列表选项,选中管理(有多种方式可以打开计算机本地服务列表,这
- substr 函数:截取字符串 语法:SUBSTR(string,start, [length])string:表示源字符串,即要
- 平时每逢alexa排名更新时,我都需要将所有相关的同类网站的排名整理一下,看一下这些对手网站的排名更新情况。做的多了,也就烦了,虽然也才30
- 本文实例讲述了Python异步操作MySQL。分享给大家供大家参考,具体如下:安装aiomysql依赖Python3.4+asyncioPy
- 1、安装coveragepip install coverage安装完成后,会在Python环境下的\Scripts下看到coverage.
- CUDACUDA是显卡厂商NVIDIA推出的运算平台。CUDA™是一种由NVIDIA推出的通用并行计算架构,是一种并行计
- 1、df=DataFrame([{‘A':'11','B':'12'},{‘A
- 用js判断复选框是否选中及选中个数,以前经常有朋友提到复选框的选中问题,这段代码可以帮你解决这个问题,而且它还可以判断你选择了多少个复选框,