GO_并发编程---Goroutine
一、前言:Go 语言的核心优势 —— 原生并发
Go 被称为21 世纪的 C 语言,两大核心亮点:
- 语法设计简洁、上手门槛低
- 语言层面原生支持并发,内置 GC 自动垃圾回收,无需手动管理内存
Go 并发设计哲学
不同于传统多线程靠锁实现同步,Go 采用通信的方式解决并发竞争:
不要通过共享内存来通信,而要通过通信来共享内存
二、基础概念:并发 vs 并行
1. 并行(Parallelism)
同一时刻,多条指令在不同 CPU 核心同时执行,必须依赖多核硬件。
2. 并发(Concurrency)
同一时刻只有一条指令在 CPU 执行,系统通过时间片轮转快速切换多个任务;宏观看起来多个任务同时运行,微观是交替执行,单核 CPU 即可实现。
举例:单核 CPU 处理 3 个 1s 任务,靠时间片切换实现并发,在有限硬件资源下提升整体处理效率。
核心区分总结
- 并发:同时处理多件事(单核、任务切换)
- 并行:同时做多件事(多核、真正同时执行)
三、Goroutine(协程):Go 并发核心
1. 什么是 Goroutine
Goroutine 是 Go 内置的轻量级协程,可简单理解为轻量任务,是 Go 并行调度的核心:
- 比操作系统线程体积更小、开销极低;
- 底层采用 M:N 调度:成千上万个 goroutine 会复用少量 OS 线程;
- Go 运行时自动管理 goroutine 内存共享,调度成本远低于线程;
- 优势:易用、高效、资源占用极少。
2. 创建 Goroutine
语法:在函数 / 匿名函数前加 go 关键字,即可新建子协程。
go
// 普通函数开启协程
go newTask()
// 匿名函数协程(必须加()执行)
go func() {
// 协程业务逻辑
}()
3. 核心规则:主协程退出,所有子协程直接销毁
main 函数本身是主协程,一旦 main 执行完毕,整个进程直接终止,所有未执行完成的子协程会被强制杀死,不会等待。
示例 1:子协程来不及执行
go
package main
import "time"
func newTask() {
for {
fmt.Println("子协程运行中")
time.Sleep(time.Second)
}
}
func main() {
// 开启子协程
go newTask()
// 主协程循环2次就退出
i := 0
for {
i++
fmt.Println("主协程 i = ", i)
time.Sleep(time.Second)
if i == 2 {
break // main结束,子协程直接销毁,无任何输出
}
}
}
示例 2:死循环阻塞主协程(测试用)
写无限for{}阻塞主协程,防止程序提前退出,方便观察子协程输出:
go
func main() {
go newTask()
// 死循环阻塞主线程
for {}
}
四、runtime 包:协程调度控制工具
1. runtime.Gosched ():主动让出 CPU 时间片
作用:当前 goroutine 主动放弃执行权,调度器切换其他就绪协程运行,等其他协程执行完毕后再切回当前协程。
示例:不加 Gosched 时,主协程会优先跑完,子协程无执行机会
go
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
for i := 0; i < 5; i++ {
fmt.Println("子协程 go")
}
}()
for i := 0; i < 2; i++ {
fmt.Println("主协程 hello")
runtime.Gosched() // 主动让出时间片,让子协程先执行
}
}
2. runtime.Goexit ():终止当前协程
作用:立即结束当前所在 goroutine,不会影响其他协程;会优先执行当前协程所有 defer 延迟函数,普通return仅终止当前函数,不会退出协程。
go
package main
import (
"fmt"
"runtime"
)
func test() {
defer fmt.Println("执行defer延迟语句")
// runtime.Goexit() // 终止整个子协程
return // 仅退出test函数,协程继续往下执行
fmt.Println("该行永远不会打印")
}
func main() {
go func() {
fmt.Println("子协程开始")
test()
fmt.Println("子协程结束") // Goexit()时该行不会执行
}()
// 死循环阻塞主协程
for {}
}
3. runtime.GOMAXPROCS (n):设置并行 CPU 核心上限
- 功能:设置可并行执行 goroutine 的 OS 线程最大数量,返回修改前的旧值;
- 规则:
- n=1:单核运行,无真正并行,仅时间片切换并发;
- n 等于 CPU 物理核数:最大化多核并行能力;
- Go1.5 + 默认值为机器 CPU 核心数;
- 效果:核数越大,多协程交替输出越混乱,并行效率越高。
go
package main
import (
"fmt"
"runtime"
)
func main() {
// 设置仅使用1核CPU,返回旧配置
old := runtime.GOMAXPROCS(1)
fmt.Println("修改前CPU并行数:", old)
for {
go fmt.Print(1)
fmt.Print(0)
}
}
五、多协程资源竞争问题
场景模拟:多个协程共用同一个打印机(公共资源)
多个 goroutine 同时操作同一共享资源,会出现输出错乱、数据混乱,即并发竞争,需要同步机制解决。
go
package main
import "fmt"
// 公共打印机资源
func Printer(str string) {
for _, data := range str {
fmt.Print(string(data))
}
fmt.Println()
}
func person1() {
Printer("hello")
}
func person2() {
Printer("world")
}
func main() {
// 两个协程同时抢占打印机,输出错乱
go person1()
go person2()
// 阻塞主协程观察效果
for {}
}
解决方案预告
Go 官方推荐使用channel 通道实现 goroutine 同步与通信,解决资源竞争,下一章详细讲解。
六、全文总结(面试 / 复习速记)
- Go 核心优势:语言原生并发,goroutine 轻量高效,GC 自动回收;
- 并发:单核时间片切换;并行:多核同时执行;
- goroutine 创建:
go + 函数,主协程退出则所有子协程销毁; - runtime 三大工具:
Gosched():主动让出 CPU;Goexit():终止当前协程,执行 defer;GOMAXPROCS():控制并行 CPU 核心数;
- 多协程访问公共资源会产生竞争,使用 channel 通信实现同步。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)