Go并发:Goroutine
1.并发基础概念:进程、线程、协程
(1) 进程
可以比作食材加工的一系列动作
进程就是程序在操作系统中的一次执行过程,是由系统进行资源分配和调度的基本单位,进程是一个动态概念,是程序在执行过程中分配和管理资源的基本单位,每一个进程都有一个自己的地址空间。一个进程至少有5种基本状态:初始态、执行态、等待状态、就绪状态、终止状态。
通俗讲: 进程就是一个正在执行的程序。
[一般情况] 一个程序一个进程
[多进程]] 一个程序多个进程
可以在终端输入以下内容,来列出所有的进程
ps -A
比如在Windows系统中,一个运行的xx.exe就是一个进程。
(2) 线程
可以比作食材加工的某一个动作
线程是运算调度的最小单元,是进程中的一个执行任务(控制单元),负责当前进程中程序的执行。一个进程至少有一个线程,一个进程可以运行多个线程,多个线程可共享数据。
进程与线程的区别如下
根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位
资源开销:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小。
包含关系:如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程。
内存分配:同一进程的线程共享本进程的地址空间和资源,而进程之间的地址空间和资源是相互独立的
影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
执行过程:每个独立的进程有程序运行的入口、顺序执行序列和程序出口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制,两者均可并发执行
查看线程
- 进入
terminal
- 输入命令
ps-M[pid]
- 查看对应
pid
线程
(3) 协程
协程(goroutine) 是轻量级的执行线程。
实操
假设我们有一个函数叫做 action()
。 我们一般会这样 同步地
调用它使用 go action()
在一个协程中调用这个函数。 这个新的 Go 协程将会 并发地
执行这个函数。
package main
import (
"fmt"
// "time"
)
func action() {
fmt.Println("Test Goroutine")
}
func main() {
go action()
// time.Sleep(2 * time.Second)
}
可见咩有任何输出,把注释内容取消注释再运行
为什么要沉睡?
go 语言运行非常快,若没有沉睡,可能会导致
“协程还没有执行完,
main
函数已经结束了”最终导致协程结束前,
main
函数已经被销毁因此我们需要让主进程去等待一定时间的子协程,才能得到子协程的输出
2.多协程核心机理
步骤
- 任务切片/分配
- 启动多个协程
- 合并多个协程结果
使用场景
- 运算量比较多
- 协程间依赖性比较弱
3.多协程等待实操
我们可以使用通道来同步协程之间的执行状态。使用 wait group 等待多个协程完成
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 2; i ++ {
wg.Add(1)
go func(num int) {
fmt.Printf("Goroutine Test %d\n", num)
wg.Done()
}(i)
}
wg.Wait()
}
更多细节清查阅
waitgroup
Go by Example 中文版: WaitGroup (gobyexample-cn.github.io)