Go 并发编程
goroutine 以非阻塞的方式执行,它们会随着程序(主线程)的结束而消亡
WaitGroup: 等待单个或多个goroutines执行结束
- 创建一个 WaitGroup 实例,比如名称为:wg
- 调用 wg.Add(n),其中 n 是等待的 goroutine 的数量
- 在每个 goroutine 运行的函数中执行 defer wg.Done()
- 调用 wg.Wait() 阻塞主逻辑
Golang 的运行时会在逻辑处理器上调度 goroutine 来运行。每个逻辑处理器都与一个操作系统线程绑定
并行 (parallelism) 是让不同的代码片段同时在不同的物理处理器上执行。并行的关键是同时做很多事情,而并发 (concurrency) 是指同时管理很多事情,这些事情可能只做了一半就被暂停去做别的事情了(Golang 的并发通过切换多个线程达到减少物理处理器空闲等待的目的)。
基于调度器的内部算法,一个正运行的 goroutine 在工作结束前,可以被停止并重新调度。调度器这样做的目的是防止某个 goroutine 长时间占用逻辑处理器。当 goroutine 占用时间过长时,调度器会停止当前正运行的 goroutine,并给其他可运行的 goroutine 运行的机会。
设置 runtime.GOMAXPROCS(1),强制让 goroutine 在一个逻辑处理器上并发执行
设置逻辑处理器的个数等于物理处理器的个数,从而让 goroutine 并行执行(物理处理器的个数得大于 1)
runtime.GOMAXPROCS(runtime.NumCPU())
进程虚拟地址空间被分成两部分:用户空间和内核空间
用户空间:
- 代码段 (可执行文件的操作指令)
- 数据段 (可执行文件中已初始化全局变量)
- BSS段 (程序中未初始化的全局变量)
- 堆 heap (进程运行中被动态分配的内存段)
- 栈 stack (程序临时创建的局部变量)
数据段、BSS 段、堆通常是被连续存储在内存中,在位置上是连续的,而代码段和栈往往会被独立存放
进程是程序资源管理的最小单位
线程是资源调度的最小单位
用户级线程: 用户级线程调度器在用户空间的线程库实现,内核的调度对象是进程本身,内核并不知道用户线程的存在。
*** 轻量级进程 Light-weight Process ***: 一种由内核支持的用户线程,每一个轻量级进程都与一个特定的内核线程
关联。
*** 协程 Coroutines *** 是一种比线程更加轻量级的微线程
协程的调度完全由用户控制,协程拥有自己的寄存器上下文和栈,协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作用户空间栈,完全没有内核切换的开销。
- G 表示 Goroutine ,它是一个待执行的任务;
- M 表示操作系统的线程,它由操作系统的调度器调度和管理;
- P 表示处理器 Processor,它可以被看做运行在线程上的本地调度器;
Context
err, deadline, value