go高并发之路——go语言如何解决并发问题
一、选择GO的原因
作为一个后端开发,日常工作中接触最多的两门语言就是PHP和GO了。无可否认,PHP确实是最好的语言(手动狗头哈哈),写起来真的很舒爽,没有任何心智负担,字符串和整型压根就不用区分,开发速度真的是比GO快很多。现在工作中也还是有一些老项目在使用PHP,但21年之后的新项目基本上就都是用GO了。那为什么PHP那么香,还要转战使用GO呢,下面就给大家讲解一下我们新项目从PHP转GO的原因,有几个比较重要的点:
1、PHP不能满足我们的高并发业务,这是最主要的原因了,(PS:我这里所说的PHP是指官方的php-fpm模式下的开发,是一个请求一个进程的那种模式,而不是类似于swoole常驻进程的那种。那么为什么不去使用swoole呢,当然也是有的,但swoole毕竟太小众了,且之前有很多bug,使用起来心智负担太高了),而我们部门所负责的是直播业务,每天都和高并发打交道啊,所以只能将目光转向了并发小王子GO的怀抱。
2、GO语言当时在市面上很火,像腾讯、百度、滴滴、好未来这些大厂都在陆陆续续地从PHP转向GO,这也是一个讯号吧,跟着大佬们走总不会错。
3、GO语言的简单简洁,相比较于JAVA,上手是很快的(但真正学好还是没那么容易的),我当时就学了两个礼拜左右语法就跟着一起写项目了。
二、GO解决的并发问题
说到并发,是GO最基本的功能了,但是在传统的PHP中是比较困难的,如果不借助其它一些扩展的话,是做不到并发的。举个场景:每个用户进入直播间,都要获取很多信息,有版本服务信息、直播基础信息、用户信息、直播关联权益信息、直播间信息统计等等。如果是PHP的写法,就得按照下面串行的流程去做,这个接口耗时就是所有操作的时间之和,严重影响用户体验啊。

但如果换成GO去做这件事,那就非常清爽了,这个用户请求耗时就只需要时间最长的那个操作耗时,如下图:

那么我们如何用去实现这个并发逻辑呢?
方法1:使用sync.WaitGroup
//请求入口
func main() {
var (
VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail int
)
ctx := context.Background()
GoNoErr(ctx, func() {
VersionDetail = 1 //版本服务信息
time.Sleep(1 * time.Second)
fmt.Println("执行第一个任务")
}, func() {
LiveDetail = 2 //直播基础信息
time.Sleep(2 * time.Second)
fmt.Println("执行第二个任务")
}, func() {
UserDetail = 3 //用户信息
time.Sleep(3 * time.Second)
fmt.Println("执行第三个任务")
}, func() {
EquityDetail = 4 //直播关联权益信息
time.Sleep(4 * time.Second)
fmt.Println("执行第四个任务")
}, func() {
StatisticsDetail = 5 //直播间信息统计
time.Sleep(5 * time.Second)
fmt.Println("执行第五个任务")
})
fmt.Println(VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail)
}
//并发方法
func GoNoErr(ctx context.Context, functions ...func()) {
var wg sync.WaitGroup
for _, f := range functions {
wg.Add(1)
// 每个函数启动一个协程
go func(function func()) {
function()
wg.Done()
}(f)
}
// 等待执行完
wg.Wait()
}
方法2:使用ErrGroup库
//请求入口
func main() {
var (
VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail int
err error
)
ctx := context.Background()
err = GoErr(ctx, func() error {
VersionDetail = 1 //版本服务信息
time.Sleep(1 * time.Second)
fmt.Println("执行第一个任务")
return nil //返回实际执行的错误
}, func() error {
LiveDetail = 2 //直播基础信息
time.Sleep(2 * time.Second)
fmt.Println("执行第二个任务")
return nil //返回实际执行的错误
}, func() error {
UserDetail = 3 //用户信息
time.Sleep(3 * time.Second)
fmt.Println("执行第三个任务")
return nil //返回实际执行的错误
}, func() error {
EquityDetail = 4 //直播关联权益信息
time.Sleep(4 * time.Second)
fmt.Println("执行第四个任务")
return nil //返回实际执行的错误
}, func() error {
StatisticsDetail = 5 //直播间信息统计
time.Sleep(5 * time.Second)
fmt.Println("执行第五个任务")
return nil //返回实际执行的错误
})
if err != nil {
fmt.Println(err)
return
}
fmt.Println(VersionDetail, LiveDetail, UserDetail, EquityDetail, StatisticsDetail)
}
func GoErr(ctx context.Context, functions ...func() error) error {
var eg errgroup.Group
for i := range functions {
f := functions[i] //请注意这里的写法,下面有讲解
eg.Go(func() (err error) {
err = f()
if err != nil {
//记日志
}
return err
})
}
// 等待执行完
return eg.Wait()
}
上面就是使用ErrGroup库的并发执行任务的方法,可以直接拿来使用,ErrGroup这是GO官方提供的一个同步扩展库,可以很好地将⼀个通⽤的⽗任务拆成⼏个⼩任务并发执⾏。
上面有一点需要特别注意的写法,就是下面这段代码的写法,写法1:
for i := range functions {
f := functions[i]
eg.Go(func() (err error) {
err = f()
也可以这样写,写法2:
for _, f := range functions {
fs := f
eg.Go(func() (err error) {
err = fs()
但如果这样写就会有问题,写法3:
for _, f := range functions {
eg.Go(func() (err error) {
err = f()
你们可以改一下,实际跑一下。会发现 (写法3) 会出现类似这样的错误结果

正确预期的结果(写法1、写法2)应该是这样的

这是因为在 Go 语言中,当使用闭包(匿名函数)时,如果闭包引用了外部的变量,闭包实际上会捕获这些变量的引用。在循环中创建闭包时,如果直接将循环变量作为闭包的参数或在闭包中引用该变量,会导致所有生成的闭包都引用相同的变量,即最后一次迭代的值。
为了避免这个问题,常见的做法是在循环内部创建一个新的变量,将循环变量的值赋给这个新变量,然后在闭包中引用该新变量。这样,每次循环迭代都会创建一个新的变量,闭包捕获的是不同的变量引用,而不是相同变量的引用。
在给定的代码中,fs := f 就是为了创建一个新的变量 f,并将循环变量 f 的值赋给它。这样,在闭包中就可以安全地引用这个新变量 f,而不会受到循环迭代的影响。这个技巧非常有用,可以在循环中创建多个独立的闭包,并确保它们捕获的是预期的变量值,而不会受到循环迭代的干扰。
当然,还有一些第三方库也实现了上面的并发分组操作,大家感兴趣的可以去GitHub上看看,但功能和实现基本都大同小异。以上就是GO并发的基础,将一个父任务拆分成多个子任务去执行,提高程序的并发度,节省程序耗时。我们平时在工作中,两种方法都可以直接拿来使用,可以说这两个GO并发方法几乎贯穿了我的GO职业生涯,也是最基础最实用的并发操作方法。
go高并发之路——go语言如何解决并发问题的更多相关文章
- GO语言开发之路
Go语言开发之路 介绍 为什么学习Go语言? 开发环境准备 从零开始搭建Go语言开发环境 VS Code配置Go语言开发环境 基础 Go语言基础之变量和常量 Go语言基础之基本数据类型 Go语言基础之 ...
- python开发之路:python数据类型(老王版)
python开发之路:python数据类型 你辞职当了某类似微博的社交网站的底层python开发主管,官还算高. 一次老板让你编写一个登陆的程序.咔嚓,编出来了.执行一看,我的妈,报错? 这次你又让媳 ...
- Java高并发之锁优化
本文主要讲并行优化的几种方式, 其结构如下: 锁优化 减少锁的持有时间 例如避免给整个方法加锁 public synchronized void syncMethod(){ othercode1(); ...
- NGINX比Apache的性能高是因为NGINX由C语言开发,而Apache由C++开发
事实上,NGINX比Apache的性能高是因为NGINX由C语言开发,而Apache由C++开发.因此,NGINX效率大概是Apache的10倍左右
- java高并发之线程池
Java高并发之线程池详解 线程池优势 在业务场景中, 如果一个对象创建销毁开销比较大, 那么此时建议池化对象进行管理. 例如线程, jdbc连接等等, 在高并发场景中, 如果可以复用之前销毁的对 ...
- 高并发之ReentrantLock、CountDownLatch、CyclicBarrier
本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...
- 高并发之Phaser、ReadWriteLock、StampedLock
本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...
- 高并发之Semaphore、Exchanger、LockSupport
本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...
- Go语言基础之并发
并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微信和两个女朋友聊天) ...
- GO学习-(18) Go语言基础之并发
Go语言基础之并发 并发是编程里面一个非常重要的概念,Go语言在语言层面天生支持并发,这也是Go语言流行的一个很重要的原因. Go语言中的并发编程 并发与并行 并发:同一时间段内执行多个任务(你在用微 ...
随机推荐
- Java 编程实例:相加数字、计算单词数、字符串反转、元素求和、矩形面积及奇偶判断
Java如何相加两个数字 相加两个数字 示例 int x = 5; int y = 6; int sum = x + y; System.out.println(sum); // 打印 x + y 的 ...
- 抓包整理————ip 协议二[十三]
前言 介绍一下什么是nat协议和napt协议,和简单带一下LVS. 正文 什么是nat(Network Address Translation) 协议呢? 比如现在你家分配了一个ip,但是你家有10个 ...
- 抓包整理————tcpdump过滤器[七]
前言 简单介绍一下tcpdump 正文 这里可以tcpdump -D 可以列出各个网卡的信息: 默认抓取eth0,也就是第一个: 还有下面的选项: -D 举例所有的网卡设备 -i 选择网卡设备 -c ...
- 国内chatGPT中文版网站有哪些?国内人工智能百花齐放!该如何选择?
人工智能技术在中国的快速发展和普及,使得国内的人工智能产业日益壮大.在这些领域中,自然语言处理技术和聊天机器人已经取得了显著的进展.ChatGPT作为一种基于深度学习的聊天机器人模型,在国内得到了广泛 ...
- node统计指定文件夹内代码行数
1. 来源 想对于自己接触前端日常学习与思考的代码行数进行一个统计,看自己大约敲了多少代码 2.代码 const fs = require('fs') const path = require('pa ...
- 力扣618(MySQL)-学生地理信息报告(困难)
题目: 一所美国大学有来自亚洲.欧洲和美洲的学生,他们的地理信息存放在如下 student 表中 该表没有主键.它可能包含重复的行.该表的每一行表示学生的名字和他们来自的大陆. 一所学校有来自亚洲.欧 ...
- 力扣415(java)-字符串相加(简单)
题目: 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回. 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换 ...
- 阿里云日志服务SLS携手观测云发布可观测性解决方案,共建可观测应用创新
简介: 2022年云栖大会期间,阿里云同观测云共同发布可观测性联合解决方案.观测云通过集成日志服务SLS的产品能力,发布了观测云SAAS专属版. 2022年云栖大会期间,阿里云同观测云共同发布可观测性 ...
- 云原生消息队列Pulsar浅析
简介: 云原生消息队列Pulsar浅析 一.前言 Pulsar是一个多租户,高性能的服务间消息解决方案.最初由Yahoo开发,现在由Apache Software Foundation负责.Pulsa ...
- 做ToB软件质量保障的这两年
简介:自己算是阿里的老兵了,从实习开始一直投身在 toB 业务的质量保障领域内,不能说是资深的专家,但所经历的.感受的业务特点和体会还是具有一定的代表性,希望能通过这篇文章,总结一下过往,并能和已经 ...