day2 golang高并发| 青训营笔记

47 阅读2分钟

# 这是我参加第五届青训营伴学笔记创作活动的第二天

讲一讲对于goroutine的学习

为什么引入goroutine

看一个需求,需要统计1-90000的数字中,哪些是素数?

  • 传统的方法,开一个for循环,循环的判断各数是不是素数(很慢)
  • 使用并发或者并行的方式,将统计素数的任务分配给多个goroutine去完成,利用多核,这时就会用到goroutine。

goroutine基本介绍

进程和线程介绍

image.png

并发和并行

image.png

  • 多线程程序在单核上运行马,就是并发
  • 多线程程序在多核上运行,就是并行

goroutine调度器

线程数过多,意味着操作系统会不断的切换线程,频繁的上下文切换就成了性能瓶颈。Go提供一种机制,可以在线程中自己实现调度,上下文切换更轻量,从而达到了线程数少,而并发数并不少的效果。而线程中调度的就是Goroutine.

早期Go版本,比如1.9.2版本的源码注释中有关于调度器的解释。Goroutine 调度器的工作就是把“ready-to-run”的goroutine分发到线程中。

Goroutine主要概念如下:

  • G(Goroutine): 即Go协程,每个go关键字都会创建一个协程。
  • M(Machine): 工作线程,在Go中称为Machine。
  • P(Processor): 处理器(Go中定义的一个摡念,不是指CPU),包含运行Go代码的必要资源,也有调度goroutine的能力。

M必须拥有P才可以执行G中的代码,P含有一个包含多个G的队列,P可以调度G交由M执行。其关系如下图所示:

image.png

图中M是交给操作系统调度的线程,M持有一个P,P将G调度进M中执行。P同时还维护着一个包含G的队列(图中灰色部分),可以按照一定的策略将不能的G调度进M中执行。

P的个数在程序启动时决定,默认情况下等同于CPU的核数,由于M必须持有一个P才可以运行Go代码,所以同时运行的M个数,也即线程数一般等同于CPU的个数,以达到尽可能的使用CPU而又不至于产生过多的线程切换开销。

程序中可以使用runtime.GOMAXPROCS()设置P的个数,在某些IO密集型的场景下可以在一定程度上提高性能。