Go使用协程批量获取数据,加快接口返回速度
服务端经常需要返回一个列表,里面包含很多用户数据,常规做法当然是遍历然后读缓存。
使用Go语言后,可以并发获取,极大提升效率。
使用channel
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优化的好。
- 这样可以保证顺序。
Go使用协程批量获取数据,加快接口返回速度的更多相关文章
- sql 根据指定条件获取一个字段批量获取数据插入另外一张表字段中+MD5加密
/****** Object: StoredProcedure [dbo].[getSplitValue] Script Date: 03/13/2014 13:58:12 ******/ SET A ...
- [Sw] 使用 Swoole Server task/协程 处理大数据量异步任务时注意
关于 Buffered Query 和 Unbuffered Query:http://www.php.net/manual/zh/mysqlinfo.concepts.buffering.php 对 ...
- go 利用chan的阻塞机制,实现协程的开始、阻塞、返回控制器
一.使用场景 大背景是从kafka 中读取oplog进行增量处理,但是当我想发一条命令将这个增量过程阻塞,然后开始进行一次全量同步之后,在开始继续增量. 所以需要对多个协程进行控制. 二.使用知识 1 ...
- 防抖与节流 & 若每个请求必须发送,如何平滑地获取最后一个接口返回的数据
博客地址:https://ainyi.com/79 日常浏览网页中,在进行窗口的 resize.scroll 或者重复点击某按钮发送请求,此时事件处理函数或者接口调用的频率若无限制,则会加重浏览器的负 ...
- python操作从数据库中获取数据的接口
1.输入一个表名,获取表里面的数据 2.判断用户是否存在,如果不存在就添加到数据库里面
- mybatis 批量导入数据到mysql返回批量Id
1.首先mybatis版本必需是3.3.1或以上 2.mapper配置文件中 <insert id="insertOrderBatch" parameterType=&quo ...
- 批量导入数据到InnoDB表速度优化
1.使用Load data: 2. SET autocommit=0; ... SQL import statements ... COMMIT; 3. SET unique_checks=0; .. ...
- 获取从天气预报接口返回回来的json数据
搬迁到了我的新博客中 ==> http://www.suanliutudousi.com/2017/08/26/%E8%8E%B7%E5%8F%96%E4%BB%8E%E5%A4%A9%E6%B ...
- 【Python】【容器 | 迭代对象 | 迭代器 | 生成器 | 生成器表达式 | 协程 | 期物 | 任务】
Python 的 asyncio 类似于 C++ 的 Boost.Asio. 所谓「异步 IO」,就是你发起一个 IO 操作,却不用等它结束,你可以继续做其他事情,当它结束时,你会得到通知. Asyn ...
- Swoole 实战:MySQL 查询器的实现(协程连接池版)
目录 需求分析 使用示例 模块设计 UML 类图 入口 事务 连接池 连接 查询器的组装 总结 需求分析 本篇我们将通过 Swoole 实现一个自带连接池的 MySQL 查询器: 支持通过链式调用构造 ...
随机推荐
- 虚拟机VMware运行Ubuntu时无法和主机之间复制粘贴的问题
解决虚拟机VMware运行Ubuntu时无法和主机之间复制粘贴的问题 执行以下命令并重启即可解决 sudo apt-get autoremove open-vm-tools sudo apt-get ...
- 【JVM】关于JVM,你需要掌握这些 | 一文彻底吃透JVM系列
写在前面 最近,一直有小伙伴让我整理下关于JVM的知识,经过十几天的收集与整理,初版算是整理出来了.希望对大家有所帮助. JDK 是什么? JDK 是用于支持 Java 程序开发的最小环境. Java ...
- Echarts - legend属性设置
legend: { orient: 'horizontal', // 'vertical' x: 'right', // 'center' | 'left' | {number}, y: 'top', ...
- SpringCloud(十) - Docker
1.Docker安装 1.1 卸载旧版本(否者会安装出错) sudo yum remove docker \ docker-client \ docker-client-latest \ docker ...
- 如何通过Java导出带格式的 Excel 数据到 Word 表格
在Word中制作报表时,我们经常需要将Excel中的数据复制粘贴到Word中,这样则可以直接在Word文档中查看数据而无需打开另一个Excel文件.但是如果表格比较长,内容就会存在一定程度的丢失,无法 ...
- 【题解】UVA10228 A Star not a Tree?
题面传送门 解决思路 本题数据范围较小,可以使用模拟退火算法(随机化). 顾名思义,模拟退火就是一个类似于降温的过程.先设置一个较大的初温,每次随机改变状态,若使答案更优,则采取更优答案,否则根据其与 ...
- 《吐血整理》高级系列教程-吃透Fiddler抓包教程(34)-Fiddler如何抓取微信小程序的包-上篇
1.简介 有些小伙伴或者是童鞋们说小程序抓不到包,该怎么办了???其实苹果手机如果按照宏哥前边的抓取APP包的设置方式设置好了,应该可以轻松就抓到包了.那么安卓手机小程序就比较困难,不是那么友好了.所 ...
- 如何使用 LinkedHashMap 实现 LRU 缓存?
本文已收录到 AndroidFamily,技术和职场问题,请关注公众号 [彭旭锐] 提问. 大家好,我是小彭. 在上一篇文章里,我们聊到了 HashMap 的实现原理和源码分析,在源码分析的过程中,我 ...
- JavaScript入门③-函数(2)原理{深入}执行上下文
00.头痛的JS闭包.词法作用域? 被JavaScript的闭包.上下文.嵌套函数.this搞得很头痛,这语言设计的,感觉比较混乱,先勉强理解总结一下. 为什么有闭包这么个东西?闭包包的是什么? 什么 ...
- 学习ASP.NET Core Blazor编程系列十五——查询
学习ASP.NET Core Blazor编程系列一--综述 学习ASP.NET Core Blazor编程系列二--第一个Blazor应用程序(上) 学习ASP.NET Core Blazor编程系 ...