在 Go 语言里,go func 是并发的单元,chan 是协调并发单元的机制,panic 和 recover 是出错处理的机制,而 defer 是神来之笔,大大简化了出错的管理。
Goroutines 在同一个用户空间里同时独立执行 functions,channels 则用于 goroutines 间的通信和同步访问控制。

前置知识

os scheduler

从操作系统角度看,我们写的程序最终都会转换成一系列的机器指令,机器只要按顺序执行完所有的指令就算完成了任务。完成“按顺序执行指令”任务的实体就是线程,也就是说,线程是 CPU 调度的实体,线程是真正在 CPU 上执行指令的实体。
每个程序启动的时候,都会创建一个初始进程,并且启动一个线程。而线程可以去创建更多的线程,这些线程可以独立地执行,CPU 在这一层进行调度,而非进程。
OS scheduler 保证如果有可以执行的线程时,就不会让 CPU 闲着。并且它还要保证,所有可执行的线程都看起来在同时执行。另外,OS scheduler 在保证高优先级的线程执行机会大于低优先级线程的同时,不能让低优先级的线程始终得不到执行的机会。OS scheduler 还需要做到迅速决策,以降低延时。

线程切换

OS scheduler 调度线程的依据就是它的状态,线程有三种状态(简化模型):Waiting, Runnable or Executing
image.png
线程能做的事一般分为两种:计算型、IO 型。
计算型主要是占用 CPU 资源,一直在做计算任务,例如对一个大数做质数分解。这种类型的任务不会让线程跳到 Waiting 状态。

IO 型则是要获取外界资源,例如通过网络、系统调用等方式。内存同步访问控制原语:mutexes 也可以看作这种类型。共同特点是需要等待外界资源就绪。IO 型的任务会让线程跳到 Waiting 状态。

线程切换就是操作系统用一个处于 Runnable 的线程将 CPU 上正在运行的处于 Executing 状态的线程换下来的过程。新上场的线程会变成 Executing 状态,而下场的线程则可能变成 Waiting 或 Runnable 状态。正在做计算型任务的线程,会变成 Runnable 状态;正在做 IO 型任务的线程,则会变成 Waiting 状态。

因此,计算密集型任务和 IO 密集型任务对线程切换的“态度”是不一样的。由于计算型密集型任务一直都有任务要做,或者说它一直有指令要执行,线程切换的过程会让它停掉当前的任务,损失非常大。

相反,专注于 IO 密集型的任务的线程,如果它因为某个操作而跳到 Waiting 状态,那么把它从 CPU 上换下,对它而言是没有影响的。而且,新换上来的线程可以继续利用 CPU 完成任务。从整个操作系统来看,“工作进度”是往前的。

记住,对于 OS scheduler 来说,最重要的是不要让一个 CPU 核心闲着,尽量让每个 CPU 核心都有任务可做。

转自:https://qcrao91.gitbook.io/go/goroutine-tiao-du-qi
系列文章全部转自: https://github.com/qcraohttps://www.cnblogs.com/abozhang