goroutine和GO的调度GPM
在讲mongodb操作的时候拿go来当了事例,那就讲讲go语言。go开始的安装和一些数据结构就不讲了,因为其实都差不多,有过编程经验的看看就能懂。个人认为go的重点就在goroutine协程上面。
讲协程之前,我们先大概的理解一下进程和线程。进程可以这么理解当你运行一个ide的时候系统就会启动一个进程,这个进程可以看作一个包含应用程序在运行中所需要用到和维护得到各种资源容器;线程的话是一个执行空间,每一个进程都至少包括一个线程,当你ide的代码在运行时就是在这个线程上运行的。操作系统在物理处理器上调度线程运行,go运行时在逻辑处理器上调度goroutine来运行。每个逻辑处理器都分别绑到单个操作系统线程上,goroutine的话也被称为用户态线程。
如果创建一个goroutine并准备运行,这个goroutine会被放到调度器的全局队列中,之后调度器会把全局队列里面的goroutine分配给M,并放到属于这个M的本地队列里面等待被运行,如下图所示。
当然有的时候goroutine在执行的时候会被阻塞,比如说打开了一个文件,当这个情况发生时,该阻塞的goroutine会被分离,放到一个M上,原来的M继续执行它本地队列里面goroutine,当被阻塞的goroutine变为非阻塞状态即准备好运行时,该goroutine会回到原来的M上继续执行;当如果goroutine因为执行网络的io调用阻塞的时候,会有点不一样,阻塞的goutine会被分离到network poller上,当该goroutine准备好时,会被重新分配到M上继续执行。
goroutine还有一个窃取机制。当多个M在并行时,有一个P上面本地队列里面的goroutine很快执行完了怎么办,它会从别的P的本地队列里面窃取一定数量的goroutine来执行,这个数量通常是队列goroutine数量一半,如果所有本地队列的goroutine全部执行完了,就会从全局队列里面去窃取goroutine来执行。
这个GPM模式就保证了为什么go会“快”一点。
上面说了那么多goroutine,那怎么创建呢,其实很简单,在go中使用go关键字就可以创建一个goroutine。一个M上面的goroutine是并发的去完成的,但是两个M上的goroutine的执行方式就是并行的了。go的main,其实就是一个goroutine是一个主协程,在这个main下面的都是子协程,我们一般运行时都不止一个逻辑处理器,这样就会有一个问题,比如说在main里面的goroutine还没有执行完成,但是main函数已经执行结束整个程序就退出了,这显然是不合理的。这种情况有几种方法可以解决。
1.就是在main函数的最后使用time.sleep()函数来等待确保所有的goroutine都已经执行完毕,再退出。这个显然不合适。
2.就是使用sync包里面的waitgroup,这个可行。
既然说到sync包就顺便来说说锁吧,其实锁的话还是用来保持数据的一致性,防止几个goroutine来共同读写某个公共资源。锁住公共资源可以用两个方法一个是atomic元子函数,另一个就是mutex类型了,具体用法不做赘述,比较简单。
以上是 goroutine和GO的调度GPM 的全部内容, 来源链接: utcz.com/z/514622.html