Go语言协程处理数据有哪些问题
作者:寻找09之夏 发布时间:2024-02-12 04:54:53
前言
我们在开发后台项目常常会遇到一个情况,功能模块列表数据导出Excel功能,但列表中某个字段无法通过Sql联表查询,且一次性查询再匹对也不方便;此时对列表数据循环,再一个个查询结果加入列表,势必需要很长的时间,我们该怎么才能提升下载速度呢? (这里采用Go开发服务端)
一、Goroutine
当然第一个想到可能是采用协程处理循环里面要查询的数据
type Card struct {
Name string `json:"name"`
Balance float64 `json:"balance"`
}
func main() {
// 获取卡列表数据
list := getList()
var data = make([]Card, 0, len(list))
for _, val := range list {
go func(card Card) {
// 查询业务,将值加入该记录中
var balance = getBalance()
data = append(data, Card{
Name: card.Name,
Balance: balance,
})
}(val)
}
log.Printf("数据:%+v", data)
}
// 获取数据列表
func getList() []Card {
var list = make([]Card, 0)
for i := 0; i < 10000; i++ {
list = append(list, Card{
Name: "卡-" + strconv.Itoa(i+1),
})
}
return list
}
// 获取余额
func getBalance() float64 {
time.Sleep(time.Millisecond * 100)
return float64(rand.Int63n(1000))
}
运行上述代码,结果: "数据:[]",这是为什么呢?主要是协程处理业务需要时间,循环提前结束,所以才会出现这样的结果,该怎么让所有结果都处理结束才输出结果呢?
二、sync.WaitGroup
此方法就是等待组进行多个任务的同步,等待组可以保证在并发环境中完成指定数量的任务
func main() {
list := getList() // 获取卡列表数据
var data = make([]Card, 0, len(list))
var wg sync.WaitGroup // 声明一个等待组
for _, val := range list {
wg.Add(1) // 每一个任务开始时,将等待组增加1
go func(card Card) {
defer wg.Done() // 使用defer, 表示函数完成时将等待组值减1
// 查询业务,休眠100微妙,将值加入该记录中
var balance = getBalance()
data = append(data, Card{
Name: card.Name,
Balance: balance,
})
}(val)
}
wg.Wait() // 等待所有任务完成
log.Printf("数据:%+v", data)
}
运行结果会输出所有数据,但细心的我们会发现,这个时候数据的顺序是乱的,这个也符合业务需求,该怎么进一步改良呢?
三、数据排序
上面讲到协程处理之后的额数据是无序的,这里我们知道数据跳数,直接初始化一个len和cap等于len(list)的空间,将之前append到data的数据改成通过下标复制,这样输出的数据就是list的数据顺序。
func main() {
list := getList() // 获取卡列表数据
var data = make([]Card, len(list), len(list))
var wg sync.WaitGroup // 声明一个等待组
for k, val := range list {
wg.Add(1) // 每一个任务开始时,将等待组增加1
go func(k int, card Card) {
defer wg.Done() // 使用defer, 表示函数完成时将等待组值减1
// 查询业务,休眠100微妙,将值加入该记录中
var balance = getBalance()
data[k] = Card{
Name: card.Name,
Balance: balance,
}
}(k, val)
}
wg.Wait() // 等待所有任务完成
log.Printf("数据:%+v", data)
}
运行上述代码,虽然可以获取到想要的数据排序,但下次下载数据较多,开的协程过多,势必导致资源开销过大,带来一系列问题,那怎么优化限制协程个数呢?
四、限制协程数
大家都知道协程过多,自然消耗过多资源,可能导致其他问题;这里我们借助chan限制协程个数
// 限制100个协程
type pool struct {
queue chan int
wg *sync.WaitGroup
}
func main() {
list := getList() // 获取卡列表数据
var data = make([]Card, len(list), len(list))
var gl = &pool{queue: make(chan int, 500), wg: &sync.WaitGroup{}} // 显示协程数最大500个
for k, val := range list {
gl.queue <- 1 // 每一个任务开始时, chan输入1个
gl.wg.Add(1) // 每一个任务开始时,将等待组增加1
go func(k int, card Card) {
defer func() {
<-gl.queue // 完成时chan取出1个
gl.wg.Done() // 完成时将等待组值减1
}()
// 查询业务,休眠100微妙,将值加入该记录中
var balance = getBalance()
data[k] = Card{
Name: card.Name,
Balance: balance,
}
}(k, val)
}
gl.wg.Wait() // 等待所有任务完成
log.Printf("数据:%+v", data)
}
通过使用chan,可以自己定义可协程最大数;现在看起来没有什么问题,但如果协程获取数据panic,会导致整个程序崩溃。
五、协程Panic处理
针对协程的panic(),我们需要接收,使用recover处理
func main() {
list := getList() // 获取卡列表数据
var data = make([]Card, len(list), len(list))
var gl = &pool{queue: make(chan int, 500), wg: &sync.WaitGroup{}} // 显示协程数最大500个
for k, val := range list {
gl.queue <- 1 // 每一个任务开始时, chan输入1个
gl.wg.Add(1) // 每一个任务开始时,将等待组增加1
go func(k int, card Card) {
// 解决协程panic,不至于程序崩溃
defer func() {
recover()
}()
defer func() {
<-gl.queue // 完成时chan取出1个
gl.wg.Done() // 完成时将等待组值减1
}()
// 查询业务,休眠100微妙,将值加入该记录中
var balance = getBalance()
data[k] = Card{
Name: card.Name,
Balance: balance,
}
}(k, val)
}
gl.wg.Wait() // 等待所有任务完成
log.Printf("数据:%+v", data)
}
// 获取余额
func getBalance() float64 {
panic("获取余额panic")
time.Sleep(time.Millisecond * 100)
return float64(rand.Int63n(1000))
}
在协程中使用defer recover();这样协程抛出来的panic被接受,不会导致程序奔溃。
来源:https://blog.csdn.net/qq_34272964/article/details/127025708


猜你喜欢
- 为了将excel数据自动转换成所需要的erlang数据,听同事说使用python会很方便简单,就自学了两天python,写了一个比较粗糙的p
- 先发官方文档的地址:官方文档学习使用的书籍是Python网络数据采集(Ryan Mitchell著),大约是一些笔记的整理。Beautifu
- 本文实例讲述了Python设置默认编码为utf8的方法。分享给大家供大家参考,具体如下:这是Python的编码问题,设置python的默认编
- 目前网络上大部分博客的结论都是这样的:Python不允许程序员选择采用传值还是传 引用。Python参数传递采用的肯定是“传对象引用”的方式
- 目录分区机制SELECT 查询INSERT 操作DELETE 操作UPDATE 操作分区的类型MySQL 的分区的实现方式是对数据表进行一层
- 在编写自动化测试用例的时候,每次登录都需要输入验证码,后来想把让python自己识别图片里的验证码,不需要自己手动登陆,所以查了一下识别功能
- 如下所示:# coding=utf-8# 用来处理数字,大于上限的数字置零f = open("/home/chuwei/桌面/tr
- 引言本文以Python3.9.1读取data.xlsx中包含的西瓜数据集3.0数据为例,数据集如下:编号色泽根蒂敲声纹理脐部触感密度含糖率好
- 1.前台ajax数据提交<form id="login_form" action="" met
- 通过一条命令用Npm安装gulp-htmlmin:npm install gulp-htmlmin --save-dev安装完毕后,打开gu
- 前言:最近写爬虫会经常遇到一些验证码识别的问题,现如今的验证码已经是五花八门,刚开始的验证码就是简单的对生成的验证码图片进行一些干扰,但是随
- 模块 定义计算机在开发过程中,代码越写越多,也就越难以维护,所以为了编写可维护的代码,我们会把函数进行分组,放在不同的文件里。在python
- SQL中的单记录函数 1.ASCII 返回与指定的字符对应的十进制数; SQL> select ascii('A')
- 安装的依赖包flaskpymysqlflask_scriptflask_migrateflask_sqlalchemy创建Flask项目(项
- 今天用vue来实现一个分页组件,总体来说,vue实现比较简单,样式部分模仿了elementUI。所有代码的源码可以再github上下载的到:
- 本例以Linux上安装Pyhton3.8版本为例进行说明1、依赖包安装yum -y install zlib-devel bzip2-dev
- 1、Python数据存储(压缩)(1)numpy.save , numpy.savez , scipy.io.savematnumpy和sc
- 情感分析(sentiment analysis)是2018年公布的计算机科学技术名词。它可以根据文本内容判断出所代表的含义是积极的还是负面的
- 什么是组件:组件是Vue.js最强大的功能之一。组件可以扩展HTML元素,封装可重用的代码。在较高层面上,组件是自定义的元素,Vue.js的
- 插入排序插入排序,英文名(insertion sort)是一种简单且有效的比较排序算法。思想: 在每次迭代过程中算法随机地从输入序