0%

为什么协程可以支持更高的并发

1、内存占用。

goroutine 栈空间大小几kb,而线程几Mb,所以在相同性能机器上,协程可以支持更多的并发量。

  • 协程栈空间大小是多少

协程可以初始时只给栈分配很小的空间,只占几kb,然后随着使用过程中的需要自动地增长。这就是为什么Go可以开千千万万个goroutine而不会耗尽内存。
当栈空间不够用时,G0协程会创建一个容量更大的栈来代替之前的栈,并且会把旧栈上的内容拷贝到新栈上去。

  • 线程的栈空间大小是多少
  1. 通过命令 ulimit -s 查看linux的默认栈空间大小,默认情况下 为10240 即10M
    我在阿里云服务器上试验后,结果为:8192
  2. 通过命令 ulimit -s 设置大小值 临时改变栈空间大小:ulimit -s 102400, 即修改为100M
  3. 可以在/etc/rc.local 内 加入 ulimit -s 102400 则可以开机就设置栈空间大小
  4. 在/etc/security/limits.conf 中也可以改变栈空间大小:soft stack 102400
  5. 重新登录,执行ulimit -s 即可看到改为102400 即100M

2、切换

协程上下文切换不需要切换到内核态,上下文切换的代价小。

  • 内核从本质上看是一种软件—控制计算机的硬件资源提供上层应用程序运行的环境。用户态即上层应用程序的活动空间,应用程序的执行必须依托于内核提供的资源,包括CPU资源、存储资源、I/O资源等。为了使上层应用能够访问到这些资源,内核必须为上层应用提供访问的接口:即系统调用。

当 threads 切换时,需要保存各种寄存器,以便将来恢复:

16 general purpose registers, PC (Program Counter), SP (Stack Pointer), segment registers, 16 XMM registers, FP coprocessor state, 16 AVX registers, all MSRs etc.

而 goroutines 切换只需保存三个寄存器:Program Counter, Stack Pointer and BP。

一般而言,线程切换会消耗 1000-1500 纳秒,一个纳秒平均可以执行 12-18 条指令。所以由于线程切换,执行指令的条数会减少 12000-18000。

Goroutine 的切换约为 200 ns,相当于 2400-3600 条指令。

因此,goroutines 切换成本比 threads 要小得多。

3、创建和销毀

Thread 创建和销毀都会有巨大的消耗,因为要和操作系统打交道,是内核级的,通常解决的办法就是线程池。而 goroutine 因为是由 Go runtime 负责管理的,创建和销毁的消耗非常小,是用户级。


  • 参考资料

https://blog.csdn.net/JH_Zhai/article/details/79861169
https://www.cnblogs.com/Peter2014/p/7883259.html
http://interview.wzcu.com/Golang/morestack.html