持续更新: github.com/Zhouchaowen… 感谢 star
系列文章-包(Package)
系列文章-变量与常量(Var)
系列文章-函数(Function)
系列文章-流程控制(For,If,Switch,defer)
系列文章-结构体(Struct)
系列文章-数组与切片(Array&Slice)
系列文章-映射(Map)
系列文章-接口(Interface)
Goroutine
Goroutine
是 Go
语言中提供并发处理的方式。在学习Goroutine
之前,我们先了解下什么是并发?什么是并行。
- 并发:一段时间内执行多个任务,这意味着仅需一个物理核心就能实现。
并发本质是我们的感知问题,就像电影闪电侠一样,普通人1s只能做一件事,而闪电侠1s可以做很多件事情,这导致我们产生了错觉,觉得闪电侠在1s内同时做这些事;
其实闪电侠只是在0.1s做了A事,在0.2s做了B事情,在0.3s做了C事,只是闪电侠做的太快了,普通人类没法在短短的0.1s感知到而已。当1s后感知到时,发现A,B,C都做完了,这就产生了闪电侠在1s内同时做这些事的错觉。
在现实中CPU
的执行是非常快的,我们通过将CPU
的执行时间划分为不同的时间片来执行任务。下图中 CPU
在1 s内并发的运行了3个任务(A,B,C
),这里将CPU
的1s执行时间划分为了5个时间片,用来分别执行这些任务。
图中在时间片1内CPU
加载任务A
并执行,当时间片1执行完后,CPU
会将任务A
移出然后开始时间片2,在时间片2中CPU
会加载任务B
并执行,接下来会按照这种方式依次执行任务C
,任务B
,任务A
;为什么还会执行任务B
和任务A
?因为任务AB
可能在时间片1和2没有执行完就被移出了,所以在后面的时间片又被加载执行。
在CUP
执行时间片中,无论任务A,B,C
本身是否执行完,当时间片耗尽后都会被移出CPU
,这个加载和移出的过程叫做上下文切换。
- 并行:同一时刻执行多个任务,这意味着至少需要2个物理核心才能实现。
并行指严格意义上的同一时刻执行2个或2个以上的任务,这取决于你的CPU
核心数。
上图中一个CPU
至少有两个物理核心Core
,每个核心上都运行着不同的任务。任务A
和任务B
分别运行在时间片1和时间片2上并且都运行在Core-1
的核心上,这意味着任务A
和任务B
是并发运行的;
而任务A
和任务F
都运行在时间片1上(代表同一时刻),并且他们分别运行在Core-1
和Core-2
的核心上,所以他们是并行运行的。
为什么说一个CPU
至少有两个物理核心Core
?
因为现代的一颗CPU上可能会有多个核心,又因为超线程技术,一个物理核心可以当做两个虚拟核心使用,所以理论上一个核心在同一时刻可以并行执行2个任务。而一般一个CPU
具有4个核心,意味着这个CPU
同一时刻可以并行运行8个任务(4*2=8)。
上文中的任务具体代表什么喃?
上文的一个任务代表一个线程,这些线程在不同的CPU
时间片上运行,这些线程都是由内核进行调度。而我们在Go
中的Goroutine
也可以看作是一个轻量级线程,它是由Go
的 Goroutine
调度器来调度的,具体的细节可以看这两篇文章来了解:Golang 调度器 GMP 原理与调度全分析和GMP 并发调度器深度解析
我们的一个Go
程序可以启动成百上千个 Goroutine
。Goroutine
的启动非常快,只需要几纳秒的时间, 而且不需要开发者手动进行线程调度,都由 Go
的Goroutine
调度器自动完成,接下来我们就来了解一下Goroutine
。
目录
Goroutine
基础用法Goroutine
中sync.WaitGroup
的使用Goroutine
小实验: 并发的下载图片Goroutine
的并发安全问题- 原子操作
- 加锁保护
Goroutine基础
Golang
中想要并发的执行一段逻辑可以通过go
关键字+匿名函数或有名函数实现, 代码如下:
// 匿名函数
go func() {
// goroutine 执行的代码
}()
// 有名函数
func test(){
fmt.Printf("golang tutorialn")
}
go test()
一个go func()
会启动一个后台并发任务, 大概流程是通过go
关键字将这个func()
打包成一个任务, 然后提交给Golang
的并发调度器, 并发调度器会根据一定策略来执行这些任务。具体的细节可以看这两篇文章来了解:Golang 调度器 GMP 原理与调度全分析和GMP 并发调度器深度解析
如下实现并发打印两个数组内的数据:
package main
import (
"fmt"
"time"
)
// 并发与并行:https://gfw.go101.org/article/control-flows-more.html
// 使用 goroutine 打印数据
func main() {
language := []string{"golang", "java", "c++", "python", "rust", "js"}
tutorial := []string{"入门", "初级", "中级", "高级", "专家"}
// Go 程(goroutine)是由 Go 运行时管理的轻量级线程
// 在函数调⽤语句前添加 go 关键字,就可创建一个 goroutine
go listLanguage(language) // 通过goroutine启动该函数
go listTutorial(tutorial)