📅 2024年5月12日
📦 使用版本为 1.21.5
ps:建议先学习操作系统,学完进程后再来看
协程
⭐️ coroutine
协程,也叫轻量级线程,或者是用户态的线程,不受操作系统直接调度
⭐️ 协程是轻量的,比线程更轻,占用的内存更少
⭐️ 一个线程中可以有任意多个协程,但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源 (操作系统知识)
⭐️ 很多语法都不直接支持协程,需要通过调用库的方式,而 Go
语言本来它就支持协程
⭐️ 在协程中,调用一个任务就像调用一个函数一样,消耗资源也少,可以达到进程、线程并发相同的效果
🌟 go
并发
⭐️ Go 在语言级别支持协程,叫 goroutine
。Go
语言标准库提供的所有系统调用操作(包括所有同步 IO
操作),都会出让 CPU
给其他 goroutine
。这让轻量级线程的切换管理不依赖于系统的线程和进程,也不需要依赖于 CPU
的核心数量
⭐️ Go
语言为并发编程而内置的上层API基于顺序通信进程模型 CSP
(communicating sequential processes
)。这就意味着显式锁都是可以避免的,因为Go通过相对安全的通道发送和接受数据以实现同步,这大大地简化了并发程序的编写
⭐️ Go语言中的并发程序主要使用两种手段来实现。goroutine
和 channel
🌟 Goroutine
⭐️ goroutine
是 Go
语言并行设计的核心,有人称之为 go
程。 Goroutine
从量级上看很像协程,它比线程更小,十几个 goroutine
可能体现在底层就是五六个线程,Go语言内部帮你实现了这些 goroutine
之间的内存共享
⭐️ 执行 goroutine
只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine
比 thread
(Java
)更易用、更高效、更轻便
1️⃣ 创建协程
⭐️ 使用 go
关键字创建协程,go
关键字后面必须是一个函数调用,下面有三种创建协程的方法,都是输出,但是协程的输出语句并不会被执行,因为启动完这三个协程主线程也就退出了,主线程退出,线程也就退出了
func main() {
go test()
go func() {
fmt.Println("我是协程2")
}()
go fmt.Println("我是协程3")
}
func test() {
fmt.Printf("我是协程1")
}
⭐️ 可以加工 sleep
等待一些协程运行,但是协程的执行顺序也是不确定的,可以多次执行下面的代码
func main() {
go test()
go func() {
fmt.Println("我是协程2")
}()
go fmt.Println("我是协程3")
//暂停1ms
time.Sleep(time.Millisecond)
}
func test() {
fmt.Printf("我是协程1\n")
}
//输出: 会发现每次执行顺序完全不一样
PS D:\learnCode\Go\gosql> go run .
我是协程3
我是协程1
我是协程2
PS D:\learnCode\Go\gosql> go run .
我是协程1
我是协程3
我是协程2
⭐️ 上面列子使用 time.Sleep
都是已经知道代码可以在多少时间内执行完毕,那如果不确定代码多久执行,就可以使用 Go
提供的其他方法
channel
: 管道WaitGroup
:信号量Context
:上下文
三种方法有着不同的适用情况,WaitGroup
可以动态的控制一组指定数量的协程,Context
更适合子孙协程嵌套层级更深的情况,管道更适合协程间通信。对于较为传统的锁控制,Go也对此提供了支持:
Mutex
:互斥锁RWMutex
:读写互斥锁
⭐️ 经典消费者问题又要来咯