Go 并发编程
并发编程
1.1 并发与并⾏

并⾏与并发是两个不同的概念,普通解释:
- 并发:交替做不同事情的能⼒
- 并⾏:同时做不同事情的能⼒
如果站在程序员的⻆度去解释是这样的:
- 并发:不同的代码块交替执⾏
- 并⾏:不同的代码块同时执⾏
并发和并⾏都是为了提⾼机器硬件利⽤率,提升应⽤运⾏效率。并⾏和并发就是为达⽬的的两个⼿段。
并⾏可以提升CPU核⼼数,并发则是通过系统设计,⽐如分时复⽤系统,多进程,多线程的开发⽅法,
不过在对并发的⽀持上,Go语⾔是天⽣最好的,因为它设计的理念就是并发,⽽且是在语⾔层⾯实现的
并发。
1.2 Goroutine
如果对线程和进程有所了解的话,我们可以这样给进程和线程下⼀个专业点的定义。
- 线程是最⼩的执⾏单位
- 进程是最⼩的资源申请单位
在Go语⾔当中,有⼀个存在是⽐线程还要⼩的执⾏单位,那就是Goroutine,翻译上习惯叫例程或协
程,不过我们遵循原汁原味还是直接⽤Goroutine来称呼它。Go语⾔开发者为了实现并⽀持
Goroutine,特意花了⼤⼒⽓来开发⼀个语⾔层⾯的调度算法。
具体调度算法详情介绍可以查看英⽂原⽂:调度算法链接
简单理解的话,⾸先要明确Go语⾔调度算法中提到的三个字⺟,M,P,G,分别代表线程,上下⽂以
及Goroutine。

在操作系统层⾯线程仍然是最⼩的执⾏单位,在进程调度的时候,CPU需要在不同进程间切换,此时需
要保留运⾏的上下⽂信息,也就是前⾯提到的P,⾄于G则是代表了Go语⾔当中的Goroutine。

每个线程M有⼀个⾃⼰的上下⽂P,同⼀时刻,每个线程内部可以执⾏⼀个Groutine代码,如上图所
示,每个线程有⼀个⾃⼰的调度队列,这个队列⾥存放的就是若⼲个Goroutine。

当线程发⽣系统调⽤时,该线程会被阻塞,此时为了提⾼运⾏效率,Goroutine调度算法会将该阻塞的
线程Goroutine队列转移到其他线程上。当线程执⾏完系统调⽤后,⼜会从其他线程的队列中“借”⼀些
Goroutine过来。
1.3 Goroutine启动
在⼀台主机上,线程启动的数量是有上限的,这个上限并⾮可以启动的上限,⽽是不影响系统性能的上
限。Goroutine在并发上则没有这⽅⾯的顾虑,可以随⼼所欲的启动,不⽤担⼼数量的问题,当然这归
功于Go语⾔的调度算法。
那么Goroutine如何启动呢?其实在我们之前写的代码中,都存在⼀个Goroutine,就是我们的主函
数,我们习惯叫他main-goroutine。如果我们想再启动⼀个Goroutine,也⾮常容易,直接关键字go再
加上函数调⽤就⾏了。
go call_func()
package mainimport "fmt"func main() {fmt.Println("begin call goroutine")//启动goroutinego func() {fmt.Println("I am a goroutine!")}()fmt.Println("end call goroutine")
}
执⾏这个代码,我们⼤概率是看不到“I am a goroutine!”这句话的,因为go func()这⾥创建的Goroutine
或者尚未创建成功时,main-goroutine已经结束执⾏了,main-goroutine结束执⾏,也就代表着进程
退出,那么不会有任何代码被执⾏了!那么⼤家考虑⼀下,如何解决这个问题呢?
1.5 Go语⾔运⾏时
所谓运⾏时,就是运⾏的时刻,Go语⾔的运⾏时就是描述与进程运⾏相关的信息,我们可以使⽤
runtime包来显示⼀些运⾏时的信息。
1.5.1 GOMAXPROCS
func GOMAXPROCS(n int) int
//当n<=1时,查看当前进程可以并⾏的goroutine最⼤数量(CPU核⼼数)
//当n>1时,代表设置可并⾏的最⼤goroutine数量
这个函数可以帮我们查看或设置当前进程的最⼤CPU核⼼数。
2. 同步
同步在不同的语境代表不同的含义,在数据库中是指数据的同步,在分布式系统中是指系统内的数据⼀
致,⽽在语⾔层⾯的同步是指运⾏时步调⼀致,避免竞争,有先有后。
2.1 如何做到同步
为了提⾼CPU的使⽤效率,我们需要启动多个Goroutine,⽽多个Goroutine⽐线程的颗粒度还⼩,他
们之间必然存在争抢同⼀资源的现象,就像我们在线程中要控制同步⼀样,多个Goroutine在访问同⼀
共享资源时,我们仍然要控制同步。
如何做到最直接的同步,可以借鉴我们⽣活中的例⼦,在⽕⻋上我们去卫⽣间的时候,都会把⻔锁上,
这样别⼈就没法进来了。在这个例⼦中,我们和其他⼈就是Goroutine,⽽卫⽣间就是那个共享资源,
我们不允许发⽣⼤家⼀起进⼊使⽤的情况,⽽解决这个问题的关键就是锁!
那么Go语⾔给我们提供了哪些同步机制呢?主要有如下⽅式:
- WaitGroup 计数等待法
- Once 执⾏⼀次
- Mutex 互斥锁
- RWMutex 读写锁
- Cond 条件变量
- channel 通道,Go语⾔的最⼤特性
在上述⽅法中,除了channel,其余的同步⽅式都在Go语⾔的sync包当中。
2.2 WaitGroup
Go语⾔的同步⽅式很多,WaitGroup的实现⽅式很巧妙,主要只有3个API。
//增加计数
func (wg *WaitGroup) Add(delta int)
//减少计数
func (wg *WaitGroup) Done()
//阻塞等待计数变为0
func (wg *WaitGroup) Wait()
它的核⼼思想是当启动⼀个Goroutine时,使⽤Add添加⼀个计数器,⽽Wait的功能是阻塞等待计数器
归0,Done的作⽤则是Goroutine运⾏结束后执⾏此句话清掉计数器的⼀个计数。
利⽤WaitGroup的特性,我们可以优雅的实现⼀个例⼦:启动10个Goroutine,让他们顺序退出,
main-goroutine等待所有Goroutine退出后才可退出。
package mainimport ("fmt""sync""time"
)var w sync.WaitGroupfunc main() {for i := 0; i < 10; i++ {w.Add(1) //添加⼀个要监控的Goroutine数量go func(num int) {time.Sleep(time.Second * time.Duration(num))fmt.Printf("I am %d Goroutine\n", num)w.Done() //释放⼀个}(i)}w.Wait() //阻塞等待
}
I am 0 Goroutine
I am 1 Goroutine
I am 2 Goroutine
I am 3 Goroutine
I am 4 Goroutine
I am 5 Goroutine
I am 6 Goroutine
I am 7 Goroutine
I am 8 Goroutine
I am 9 Goroutine
2.3 Mutex互斥锁
提到互斥锁,我们通常会提到⼀个临界区的概念,Goroutine在准备访问共享数据时,我们就认为它进
⼊了临界区。
使⽤互斥锁的核⼼思想就是进⼊临界区之前先要申请锁,申请到锁的Goroutine继续执⾏,⽽没有申请
到的Goroutine则阻塞等待别⼈释放这个mutex,这样就可以有效的控制Goroutine之间竞争的问题。
下述代码就是⼀个存在数据修改竞争的例⼦,循环1000次对⼀个数据⾃增,⽬标的输出结果是1000,
但执⾏下⾯的代码很难获得1000。
package mainimport ("fmt""sync"
)var x = 0func increment(wg *sync.WaitGroup) {x = x + 1wg.Done()
}
func main() {var w sync.WaitGroupfor i := 0; i < 1000; i++ {w.Add(1)go increment(&w)}w.Wait()fmt.Println("final value of x", x)
}
final value of x 974
上述代码如果在进⼊临界区前使⽤mutex,就可以很好的解决该问题。代码主要修改increment函数即
可:
func increment(wg *sync.WaitGroup) {mutex.Lock() //上锁x = x + 1 //临界区mutex.Unlock() //释放锁wg.Done()
}
修改后再执⾏代码,就可以很好的看到效果。
final value of x 1000
2.4 RWMutex
RWMutex我们可以称其为读写锁,它算是mutex的改进版,因为mutex的特点是排他性,只要有⼀个
上锁成功了,其余⼈都不可以使⽤。但在实际开发过程中,经常会出现多个Goroutine去访问相同的共
享资源,只不过这些Goroutine中有些是读数据,有些是写数据。开发者肯定明⽩,读数据不会对数据
造成影响,这样理论上来说,⼀个读的Goroutine上锁了,其余的读Goroutine理应也可以访问,这样
就出现了读写锁。对于读写锁来说,关键是掌握它的原则:
- 读共享
- 写独占
- 写优先级⾼
package mainimport ("fmt""sync""time"
)var rwlock sync.RWMutex
var wg sync.WaitGroup
var x = 0func go_reader(num int) {for {rwlock.RLock()fmt.Printf("I am %d reader goroutine x = %d\n", num, x)time.Sleep(time.Millisecond * 2)rwlock.RUnlock()}wg.Done()
}
func go_writer(num int) {for {rwlock.Lock()x += 1fmt.Printf("I am %d writer goroutine x = %d\n", num, x)time.Sleep(time.Millisecond * 2)rwlock.Unlock()}wg.Done()
}
func main() {wg.Add(10)for i := 0; i < 7; i++ {go go_reader(i)}for i := 0; i < 3; i++ {go go_writer(i)}wg.Wait()
}
2.5 Once
在很多时候,我们会有⼀个需求,那就是虽然多个Goroutine都要运⾏⼀段代码,但我们却希望这段代
码只能被⼀个Goroutine运⾏,也就是说只被允许运⾏⼀次。在Go语⾔当中,就给我们提供了这样的机
制 – Once。
package mainimport ("fmt""sync"
)func main() {var count intvar once sync.Oncevar wg sync.WaitGroupfor i := 0; i < 100; i++ {wg.Add(1)go func() {once.Do(func() {count += 1 //确保只执⾏⼀次})wg.Done()}()}wg.Wait()fmt.Printf("Count is %d\n", count)
}
Count is 1相关文章:
Go 并发编程
并发编程 1.1 并发与并⾏ 并⾏与并发是两个不同的概念,普通解释: 并发:交替做不同事情的能⼒并⾏:同时做不同事情的能⼒ 如果站在程序员的⻆度去解释是这样的: 并发:不同的代码块交替执⾏并⾏…...
鱼眼相机去畸变(图像拉直/展开/矫正)算法及实战总结
本文介绍两种方法 1、经纬度矫正法 2、棋盘格矫正法 一、经纬度矫正法 1、算法说明 经纬度矫正法, 可以把鱼眼图想象成半个地球, 然后将地球展开成地图,经纬度矫正法主要是利用几何原理, 对图像进行展开矫正。 经过P点的入射光线…...
es6 数据类型
es6 数据类型 map 数据类型 >Map 对象保存键值对。 用途 : Object的key无法支持该数据时需要了解对象大小时 map 数据类型任何值(对象或者原始值) 都可以作为一个键。 Object 的键只能是字符串 let myMap new Map(); let myMap1 new Map(); var keyStrin…...
【postgresql】
看到group by 1,2 和 order by 1, 2。看不懂,google,搜到了Stack Overflow 上有回答 What does SQL clause “GROUP BY 1” mean? 大概意思就是,group by, order by 后面跟数字,指的是 selec…...
【C++】空间配置器 allocator:原理及底层解析
文章目录 空间配置器一级空间配置器二级空间配置器1. 内存池2. SGI-STL中二级空间配置器设计 - - 哈希桶3. 二级空间配置器的空间申请 空间配置器的默认选择空间配置器的在封装:添加了数据类型大小空间配置器对象的构造与析构 容器中的 allocator 空间配置器 提到空…...
微信小程序 movable-area 区域拖动动态组件演示
movable-area 组件在小程序中的作用是用于创建一个可移动的区域,可以在该区域内拖动视图或内容。这个组件常用于实现可拖动的容器或可滑动的列表等交互效果。 使用 movable-area 组件可以对其内部的 movable-view 组件进行拖动操作,可以通过设置不同的属…...
隔离上网,安全上网
SDC沙盒数据防泄密系统(安全上网,隔离上网) •深信达SDC沙盒数据防泄密系统,是专门针对敏感数据进行防泄密保护的系统,根据隔离上网和安全上网的原则实现数据的代码级保护,不会影响工作效率,不…...
NOSQL Redis 数据持久化 RDB、AOF(二) 恢复
redis 执行flushall 或 flushdb 也会产生dump.rdb文件,但里面是空的。 注意:千万执行,不然rdb文件会被覆盖的。 dump.rdb 文件如何恢复数据 讲备份文件 dump.rdb 移动到redis安装目录并启动服务即可。 dump.rdb 自动触发 和手动触发 自…...
UDP通信
UDP通信 #include <sys/types.h> #include <sys/socket.h> ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen); - 参数:- sockfd : 通信的fd- buf : 要发送的数据- len : 发送…...
Bootstrap对溢出内容的两种处理:滚动条和隐藏两种方式
Bootstrap中定义了以下两个类来处理内容溢出的情况: 类overflow-auto:在固定宽度和高度的元素上,如果内容溢出了元素,将生成一个垂直滚动条,通过滚动条可以查看溢出的内容。 类overflow-hidden:在固定宽度和高度的元素…...
elasticsearch基本语法
这里写自定义目录标题 elasticsearch简介基本语法索引创建索引修改索引删除索引 查询简单查询精确查询条件查询范围查询:聚合查询:排序和分页: 参考文献: elasticsearch简介 Elasticsearch 是一个开源的分布式搜索和分析引擎&…...
Maven Spring jar包启动报错 排查
Maven Spring jar包启动报错排查 背景 maven 编译jar包,放在linux服务器启动不起来,提示:xxxx-0.0.1-SNAPSHOT.jar中没有主清单属性 原因 pom 配置文件,多了 <skip>true</skip> <build><plugins>&l…...
LeetCode-2485-找出中枢整数
题目描述: 给你一个正整数 n ,找出满足下述条件的 中枢整数 x : 1 和 x 之间的所有元素之和等于 x 和 n 之间所有元素之和。 返回中枢整数 x 。如果不存在中枢整数,则返回 -1 。题目保证对于给定的输入,至多存在一个中…...
nano pi m1配置脚本(全志H3)
为nanopi m1写一个自动配置脚本,简化自己的操作 配置:H3芯片,1G内存,64G卡 系统:friendlycore focal 4.14版本 一、系统安装 烧录系统后,插入机器,但是使用df -ih发现只有900K的nodesÿ…...
linux--gdb的使用
1,Makefile默认release版本,要想进入debug版本需添加-g后缀 2,进入调试界面:gdb 可执行程序 3,显示代码:l(list) 数字(1/0) 不停回车可一直显示到结束并显…...
JVM命令行监控工具
JVM命令行监控工具 概述 性能诊断是软件工程师在日常工作中需要经常面对和解决的问题,在用户体验至上的今天,解决好应用的性能问题能带来非常大的收益。 Java作为最流行的编程语言之一,其应用性能诊断一直受到业界广泛关注,可能…...
系统架构设计:4 论微服务架构及其应用
目录 一 微服务架构 1 微服务 2 微服务架构的优点 3微服务面临的挑战...
【C++设计模式之建造者模式:创建型】分析及示例
简介 建造者模式(Builder Pattern)是一种创建型设计模式,它将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。 描述 建造者模式通过将一个复杂对象的构建过程拆分成多个简单的部分,并由不同…...
C++day03(动态内存、类中特殊成员函数)
今日任务 1> 思维导图 2> 设计一个Per类,类中包含私有成员:姓名、年龄、指针成员身高、体重,再设计一个Stu类,类中包含私有成员:成绩、Per类对象p1,设计这两个类的构造函数、析构函数和拷贝构造函数。 代码: …...
【Leetcode】179. 最大数
一、题目 1、题目描述 给定一组非负整数 nums,重新排列每个数的顺序(每个数不可拆分)使之组成一个最大的整数。 注意:输出结果可能非常大,所以你需要返回一个字符串而不是整数。 示例1: 输入:nums = [10,2] 输出:"210"示例2: 输入:nums = [3,30,34,5…...
Spring Boot 实现流式响应(兼容 2.7.x)
在实际开发中,我们可能会遇到一些流式数据处理的场景,比如接收来自上游接口的 Server-Sent Events(SSE) 或 流式 JSON 内容,并将其原样中转给前端页面或客户端。这种情况下,传统的 RestTemplate 缓存机制会…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
最新SpringBoot+SpringCloud+Nacos微服务框架分享
文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的,根据Excel列的需求预估的工时直接打骨折,不要问我为什么,主要…...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
Java面试专项一-准备篇
一、企业简历筛选规则 一般企业的简历筛选流程:首先由HR先筛选一部分简历后,在将简历给到对应的项目负责人后再进行下一步的操作。 HR如何筛选简历 例如:Boss直聘(招聘方平台) 直接按照条件进行筛选 例如:…...
CSS设置元素的宽度根据其内容自动调整
width: fit-content 是 CSS 中的一个属性值,用于设置元素的宽度根据其内容自动调整,确保宽度刚好容纳内容而不会超出。 效果对比 默认情况(width: auto): 块级元素(如 <div>)会占满父容器…...
HarmonyOS运动开发:如何用mpchart绘制运动配速图表
##鸿蒙核心技术##运动开发##Sensor Service Kit(传感器服务)# 前言 在运动类应用中,运动数据的可视化是提升用户体验的重要环节。通过直观的图表展示运动过程中的关键数据,如配速、距离、卡路里消耗等,用户可以更清晰…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...
