Go语言协程(21)| Go主题月

678 阅读5分钟

在Go语言中可以说最核心的是协程(goroutine)了,Go语言原生的支持协程让它的效率非常高,说协程之前先回顾一下线程和进程。

进程和线程

在操作系统中,进程是一个非常重要的概念,我现在正在用的是linux,当启动一个软件的时候就会为这个软件启动一个进程,可以使用linux命令top查看计算机启动进程的状态:

image.png 第一列就是进程号,command那一列就是软件的名字,一个软件启动通常会同时执行很多不同的任务,比如QQ音乐最少有一个任务显示界面,有一个任务播放音频,还有任务缓存下一首歌等等,所以可以使用ps -T -p 进程号可以查看进程下启动的线程:

image.png 可以看到一个进程下面有很多线程,有一个主线程,PID为24167,其他都是子线程,在折腾linux的时候有时候有些软件卡了,或想彻底卸载某个软件通常会使用kill -9 进程号给这个进程发送强制关闭的信号彻底关闭软件,一个进程下面的线程是资源分配的最小单位,同一进程下不同线程间数据共享很容易开销很小,所以运行软件不会启动很多进程而会启动一两个进程,在这些进程里面再启动一些线程,线程消耗的资源会比进程小很多。

并行和并发

现在的计算机核心都很多了,在启动很多软件的时候随时都会有并行和并发的现象发生。并发并行操作表面上都可以看作在同一时间做很多事情。但是并发其实并不算同一时间做同一件事情,如果把这个时间压缩到很小的情况来他们还是在互相等待一一切换交替执行任务的,只是计算机在不同任务中切换得太快了,让人感觉不到等待,经常听说的一个词就IO多路复用就是并发操作。
并发这个词相关的就是并行,并行就是在同一时间点同时互不干扰地执行任务,注意是时间点,很小的时间概念,并发和并行我觉得差别很大的就是并发会互相影响,自己通常在编写程序的时候都是串行执行,按照编写的代码从头执行到尾,遇到多线程操作的代码的时候控制不知道要开启多少个线程操作,因为线程在进程下执行任务会互相争抢计算资源,自己不知道自己写的那些线程执行相同任务哪个任务会最完成,一切交给天意。

协程

进程和线程都是计算机系统来控制切换的,用户写代码不能控制他们的切换,但是协程是用户控制的,Go语言的并发是己调度的,自己决定同时执行多少个goroutine,什么时候执行哪一个,所以在系统命令中是没有查看协程这个命令的,可以把协程看作一个微型的线程,线程会争抢进程向系统申请的资源,例如线程大概分成5种状态,创建,就绪,执行,阻塞,死亡,与其他进程互相竞争或者互相等待经常会改变线程的状态,做一些无用功(对执行的任务来说)。在而协程中就是可以看作是协作式的,协程切换由Go语言自己特有的模型逻辑控制,在这个切换控制逻辑中执行协程任务时默认启用计算机全部的核心,在每个核心在启动分配若干个线程,再在这些若干个线程中启动很多个协程(goroutine)(其中有个调度的核心点:这些若干个进程每个进程还要维护一个协程任务队列,有的线程里的协程任务做完了会把其他线程的协程任务拿过来执行,保证所有人都有活干,充分榨干计算机性能!)详情可以百度搜索一下GMP 模型(goroutine,Machine,Processor)还有很多调度细节。

简单代码实现

说了那么多怎么实现一个携程呢?Go语言协程代码非常简单,使用go关键字就可以做到,不需要导入包。不像PHP实现携程还经常使用swoole,Python还需要导入asyncio包来实现异步协程,看看Go语言实现协程真实代码:

package main

import (
	"fmt"
	"time"
)

func main() {
	go fmt.Println("我是土味挖掘机创建的协程任务!")
	fmt.Println("我是 主进程")
	time.Sleep(time.Second)
}

运行结果:

image.png 21篇Go语言学习总结终于完成,虽然自己感觉很多地方写的不怎好导致没多少人互动,但是借一句Go语言活动群友的话:写文章是个自悦的方式,不用写的很完美,所以非常有成就感,感觉学到了很多东西,本来是打算只写七篇的,越写越来起劲。接下来会再写一些关于Go的文章,敬请期待,哈哈。