进程、线程、协程
从内存、执行单元以及切换成本三个角度来考虑
内存
-
进程
操作系统会以进程为单位,分配系统资源(CPU时间片、内存等资源),进程是资源分配的最小单位。 进程的安全性比较⾼ 通信成本就比较⼤(通过IPC): 信号 管道 共享内存 socket⽹络通信 共享⽂件
-
线程
操作系统调度(CPU调度)执⾏的最小单位。 共享资源,隔离性差 通信成本比较小 通过同步机制访问共享资源即可完成通信 线程是寄生在进程之上的
执行单元
- 操作系统并不区分进程和线程区别
- 对于同⼀个操作系统⽽⾔,如果⼀个进程开辟的线程多,那么抢占到cpu的资源就越多
切换成本
开销
-
线程切换
保存寄存器中的内容 CPU⾼速缓存失效
-
协程切换
⽤户态下的切换,实际上就是⼀个寄存器的加载
(1)协程切换完全在⽤户空间进⾏,线程切换涉及特权模式切换,需要在内核空间完成
(2)协程切换相⽐线程切换做的事情更少,线程需要有内核和⽤户态的切换,系统调⽤过程
(3)⼀般⼀个协程切换的时间在⼏⼗ns(纳秒量级),⼀个线程切换时间在⼏⼗~千μs(微秒量级)
大小
- 进程 GB量级
- 线程 MB量级
- 协程 KB量级
不控制goroutine数量引发的问题
Goroutine
- 体积轻量
- 优质的GMP调度
无限开辟goroutine
- CPU的使⽤率上升
- Memory占⽤不断上升
- 主进程崩溃(被强制杀死)
限制goroutine数量的方法
方法一: 只使用buffer和channel来限制
- 弊端:如果task任务少,main先退出,由于没有同步机制,main会导致进程退出,很多在跑的go就会强制退出,任务执⾏不全。
方法二: 只使用 sync同步机制 来限制
单纯使⽤sync的waitGroup是⽆法限制go的数量的!
方法三: channel和sync的组合方式来限制
在⽅法⼀的基础上,加上同步机制。
方法四:无缓冲channel和任务发送/执行分离来限制(工作池)
将任务的发送和执⾏做了业务上的分离
- 输⼊SendTask的频率可以设置
- 输出执⾏task的Goroutins是数量也是可以设置