前言

并发编程一直是Golang区别与其他语言的很大优势,也是实际工作场景中经常遇到的。近日笔者在组内分享了我们常见的并发场景,及代码示例,以期望大家能在遇到相同场景下,能快速的想到解决方案,或者是拿这些方案与自己实现的比较,取长补短。现整理出来与大家共享。
简单并发场景
很多时候,我们只想并发的做一件事情,比如测试某个接口的是否支持并发。那么我们就可以这么做:
func RunScenario1() {
count := 10
var wg sync.WaitGroup
for i := 0; i < count; i++ {
wg.Add(1)
go func(index int) {
defer wg.Done()
doSomething(index)
}(i)
}
wg.Wait()
}
使用goroutine来实现异步,使用WaitGroup来等待所有goroutine结束。这里要注意的是要正确释放WaitGroup的counter(在goroutine里调用Done()方法)。
但此种方式有个弊端,就是当goroutine的量过多时,很容易消耗完客户端的资源,导致程序表现不佳。
规定时间内的持续并发模型
我们仍然以测试某个后端API接口为例,如果我们想知道这个接口在持续高并发情况下是否有句柄泄露,这种情况该如何测试呢?
这种时候,我们需要能控制时间的高并发模型:
func RunScenario2() {
timeout := time.Now().Add(time.Second * time.Duration(10))
n := runtime.NumCPU()
waitForAll := make(chan struct{})
done := make(chan struct{})
concurrentCount := make(chan struct{}, n)
for i := 0; i < n; i++ {
concurrentCount <- struct{}{}
}
go func() {
for time.Now().Before(timeout) {
<-done
concurrentCount <- struct{}{}
}
waitForAll <- struct{}{}
}()
go func() {
for {
<-concurrentCount
go func() {
doSomething(rand.Intn(n))
done <- struct{}{}
}()
}
}()
<-waitForAll
}
上面的代码里,我们通过一个buffered channel来控制并发的数量(concurrentCount),然后另起一个channel来周期性的发起新的任务,而控制的条件就是 time.Now().Before(timeout),这样当超过规定的时间,waitForAll 就会得到信号,而使整个程序退出。
这是一种实现方式,那么还有其他的方式没?我们接着往下看。
基于大数据量的并发模型
前面说的基于时间的并发模型,那如果只知道数据量很大,但是具体结束时间不确定,该怎么办呢?
比如,客户给了个几TB的文件列表,要求把这些文件从存储里删除。再比如,实现个爬虫去爬某些网站的所有内容。
而解决此类问题,最常见的就是使用工作池模式了(Worker Pool)。以删文件为例,我们可以简单这样来处理:
Jobs - 可以从文件列表里读取文件,初始化为任务,然后发给worker
Worker - 拿到任务开始做事
Collector - 收集worker处理后的结果
Worker Pool - 控制并发的数量
虽然这只是个简单Worker Pool模型,但已经能满足我们的需求:
func RunScenario3() {
numOfConcurrency := runtime.NumCPU()
taskTool := 10
jobs := make(chan int, taskTool)
results := make(chan int, taskTool)
var wg sync.WaitGroup
// workExample
workExampleFunc := func(id int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
defer wg.Done()
for job := range jobs {
res := job * 2
fmt.Printf("Worker %d do things, produce result %d \n", id, res)
time.Sleep(time.Millisecond * time.Duration(100))
results <- res
}
}
for i := 0; i < numOfConcurrency; i++ {
wg.Add(1)
go workExampleFunc(i, jobs, results, &wg)
}
totalTasks := 100 // 本例就要从文件列表里读取
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < totalTasks; i++ {
n := <-results
fmt.Printf("Got results %d \n", n)
}
close(results)
}()
for i := 0; i < totalTasks; i++ {
jobs <- i
}
close(jobs)
wg.Wait()
}
在Go里,分发任务,收集结果,我们可以都交给Channel来实现。从实现上更加的简洁。
仔细看会发现,本模型也是适用于按时间来控制并发。只要把totalTask的遍历换成时间控制就好了。
等待异步任务执行结果
goroutine和channel的组合在实际编程时经常会用到,而加上Select更是无往而不利。
func RunScenario4() {
sth := make(chan string)
result := make(chan string)
go func() {
id := rand.Intn(100)
for {
sth <- doSomething(id)
}
}()
go func() {
for {
result <- takeSomthing(<-sth)
}
}()
select {
case c := <-result:
fmt.Printf("Got result %s ", c)
case <-time.After(time.Duration(30 * time.Second)):
fmt.Errorf("指定时间内都没有得到结果")
}
}
在select的case情况,加上time.After()模型可以让我们在一定时间范围内等待异步任务结果,防止程序卡死。
定时反馈异步任务结果
上面我们说到持续的压测某后端API,但并未实时收集结果。而很多时候对于性能测试场景,实时的统计吞吐率,成功率是非常有必要的。
func RunScenario5() {
concurrencyCount := runtime.NumCPU()
for i := 0; i < concurrencyCount; i++ {
go func(index int) {
for {
doUploadMock()
}
}(i)
}
t := time.NewTicker(time.Second)
for {
select {
case <-t.C:
// 计算并打印实时数据
}
}
}
这种场景就需要使用到Ticker,且上面的Example模型还能控制并发数量,也是非常实用的方式。
知识点总结
上面我们共提到了五种并发模式:
归纳下来其核心就是使用了Go的几个知识点:Goroutine, Channel, Select, Time, Timer/Ticker, WaitGroup. 若是对这些不清楚,可以自行Google之。
另完整的Example 代码可以参考这里:https://github.com/jichangjun/golearn/blob/master/src/carlji.com/experiments/concurrency/main.go
使用方式: go run main.go <场景>
比如 :
参考文档
https://github.com/golang/go/wiki/LearnConcurrency
这篇是Google官方推荐学习Go并发的资料,从初学者到进阶,内容非常丰富,且权威。
Contact me ?
Email: jinsdu@outlook.com
Blog: http://www.cnblogs.com/jinsdu/
Github: https://github.com/CarlJi
# Go并发
# Go并发编程中sync/errGroup的使用
# Go并发编程之sync.Once使用实例详解
# Go并发编程之goroutine使用正确方法
# golang 并发编程之生产者消费者详解
# GO语言并发编程之互斥锁、读写锁详解
# go并发编程sync.Cond使用场景及实现原理
# 时间内
# 文件列表
# 我们可以
# 为例
# 来实现
# 的是
# 后端
# 进阶
# 几个
# 就会
# 是个
# 有个
# 让我们
# 句柄
# 还能
# 都没
# 取长补短
# 遍历
# 其他的
# 适用于
相关文章:
建站之星logo尺寸如何设置最合适?
实例解析Array和String方法
如何选择CMS系统实现快速建站与SEO优化?
如何快速上传建站程序避免常见错误?
制作旅游网站html,怎样注册旅游网站?
深入理解Android中的xmlns:tools属性
5种Android数据存储方式汇总
保定网站制作方案定制,保定招聘的渠道有哪些?找工作的人一般都去哪里看招聘信息?
常州企业建站如何选择最佳模板?
网站制作怎么样才能赚钱,用自己的电脑做服务器架设网站有什么利弊,能赚钱吗?
香港服务器网站搭建教程-电商部署、配置优化与安全稳定指南
专业企业网站设计制作公司,如何理解商贸企业的统一配送和分销网络建设?
魔方云NAT建站如何实现端口转发?
如何用免费手机建站系统零基础打造专业网站?
如何彻底删除建站之星生成的Banner?
C++如何使用std::optional?(处理可选值)
网站制作哪家好,cc、.co、.cm哪个域名更适合做网站?
建站之星安装后如何配置SEO及设计样式?
如何快速选择适合个人网站的云服务器配置?
想学网站制作怎么学,建立一个网站要花费多少?
非常酷的网站设计制作软件,酷培ai教育官方网站?
如何在阿里云香港服务器快速搭建网站?
已有域名和空间,如何快速搭建网站?
早安海报制作网站推荐大全,企业早安海报怎么每天更换?
建站VPS配置与SEO优化指南:关键词排名提升策略
网站制作价目表怎么做,珍爱网婚介费用多少?
武汉网站设计制作公司,武汉有哪些比较大的同城网站或论坛,就是里面都是武汉人的?
制作网站的公司有哪些,做一个公司网站要多少钱?
如何在云服务器上快速搭建个人网站?
如何在橙子建站上传落地页?操作指南详解
如何打造高效商业网站?建站目的决定转化率
Python路径拼接规范_跨平台处理说明【指导】
网站好制作吗知乎,网站开发好学吗?有什么技巧?
制作网站公司那家好,网络公司是做什么的?
php条件判断怎么写_ifelse和switchcase的使用区别【对比】
岳西云建站教程与模板下载_一站式快速建站系统操作指南
如何通过虚拟机搭建网站?详细步骤解析
微信h5制作网站有哪些,免费微信H5页面制作工具?
公司网站制作需要多少钱,找人做公司网站需要多少钱?
较简单的网站制作软件有哪些,手机版网页制作用什么软件?
金*站制作公司有哪些,金华教育集团官网?
PHP正则匹配日期和时间(时间戳转换)的实例代码
开心动漫网站制作软件下载,十分开心动画为何停播?
网站制作企业,网站的banner和导航栏是指什么?
个人摄影网站制作流程,摄影爱好者都去什么网站?
广德云建站网站建设方案与建站流程优化指南
韩国服务器如何优化跨境访问实现高效连接?
制作网站的模板软件,网站怎么建设?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
安徽网站建设与外贸建站服务专业定制方案
*请认真填写需求信息,我们会在24小时内与您取得联系。