葡萄完整视频:https://segmentfault.com/a/11...原视频地址:https://biglive.xueersi。com/L...GO协程相关知识(扩展)Go语言最大的特点就是从语言层面支持并发(Goroutine)。Goroutine是Go中最基本的执行单元。事实上,每个Go程序都至少有一个Goroutine:mainGoroutine。它是在程序启动时自动创建的。首先了解一下什么是协程,什么是线程协程:又叫微线程,和子程序(或函数)一样,协程也是程序组件。与子程序相比,协程更加通用和灵活,但在实际应用中并没有子程序那么广泛。与线程类似,共享堆不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外开销,兼顾了多线程的优势,简化了高并发程序的复杂度。Goroutine可以理解为Go语言中的协程。同时它可以运行在一个或多个线程上。线程:LightweightProcess(LWP),是程序执行流程的最小单位。一个标准线程由线程ID、当前指令指针(PC)、寄存器集和堆栈组成。另外,线程是进程中的一个实体,是系统独立调度调度的基本单位。线程本身不拥有系统资源,只拥有运行所必需的少数资源。但是,它可以与属于同一进程的其他资源进行交互,线程共享该进程拥有的所有资源。线程有自己独立的栈和共享堆,共享堆,不共享栈,线程切换一般由操作系统调度。区别:协程运行在线程上,在代码中显示控制,没有上下文切换消耗的资源。协程有堆栈空间来存储它们自己的上下文。如果栈空间不足,就会改其他协程。Go实现了两种形式的并发。第一个是公认的:多线程共享内存。其实就是Java或者C++等语言的多线程开发。另一个是Go语言特有的,Go语言推荐的:CSP(communictingsequentialprocesses)并发模型。CSP并发模型:通过通信方式共享内存。传统的多线程使用共享内存进行通信,而GO语言使用共享内存进行通信。说说这个CSP模型,是通过goroutine和channel来实现的。-Goroutine是Go语言中的并发执行单元。有点抽象,其实类似于传统的“线程”概念,可以理解为“线程”。-channel是Go语言中各个并发结构(goroutine)之前的通信机制。通俗地说,就是goroutine之间进行通信的“管道”,有点类似于linux中的pipeline。生成goroutine需要简单的gofunc()代码来生成,channel也需要声明chan。同时,您在使用频道时需要注意。读写时,通道被阻塞。写入管道或读取数据类似于流式输入和输出。GO并发的实现原理是从线程开始的。无论语言层面是怎样的并发模型,在操作系统层面都必须以线程的形式存在。操作系统根据资源访问权限的不同可分为用户空间和内核空间;内核空间主要操作和访问CPU资源、I/O资源、内存资源等硬件资源,为上层应用提供最基本的基础资源。,用户空间是上层应用程序的固定活动空间。用户空间不能直接访问资源,必须通过“系统调用”、“库函数”或“Shell脚本”调用内核空间提供的资源。我们现在的计算机语言可以看作是一种狭义的“软件”。其中所谓的“线程”往往是用户态线程,与操作系统本身的内核态线程(简称KSE)还是有区别的。也就是说不管你怎么实现并发,基本上都是调用内核态线程。所有的并发都是纸老虎~线程模型的实现可以分为以下几种方式:1.用户级线程模型,2.内核级线程模型,3.二级线程模型。而我们的GO线程模型是一种特殊的二级线程模型(GPM调度模型)。GPM调度模型:G:代表goroutine,存储goroutine执行栈信息、goroutine状态、goroutine任务函数等;P:代表logicprocessor,P的个数决定了系统最大并行G个数(前提:系统物理cpu核数>=P个数);P最大的作用就是它的各种G对象队列,链表,一些缓存和状态。M:M代表真正的执行计算资源。绑定一个有效的p后,进入schedule循环;schedule循环的机制大致是从各个队列和p的本地队列获取G,切换到G的执行栈执行G的函数,调用goexit做清理工作并返回m,等等。M不保留G的状态,这是G跨M调度的基础。P是一个“逻辑处理器”。如果每个G要真正跑起来,首先要给它分配一个P(输入P的localrunq,这里忽略globalrunq的链接)。对于G,P是运行它的“CPU”。可以说G眼里只有P。但是从Go调度器的角度来看,真正的“CPU”是M,只有P和M绑定,P的runq中的G才能真正运行起来。G-P-M模型的实现对于Go调度器来说是一个很大的进步,但是Scheduler仍然有一个让人头疼的地方,就是不支持抢占式调度,以至于一旦某个G中出现了死循环或者永恒循环的代码逻辑,那么G将被永久占用。分配给它的P和M,同一个P中的其他G不会被调度,就会出现“饿死”的情况。更严重的是,当只有一个P(GOMAXPROCS=1)时,整个围棋程序中的其他G都会“饿死”。那我们该怎么办呢?在GO1.2中,他增加了一个监视器。Go程序启动时,runtime会启动一个名为sysmon的m(一般称为监控线程)。这个m可以在不绑定p的情况下运行,这个m在整个Go程序的运行过程中都非常重要:对长时间运行的G任务发出抢占式调度,通过syscall恢复已经阻塞了很长时间的P,etc如果G阻塞在一个系统调用操作上,不仅G会阻塞,这个G的M的执行也会解除P的绑定(本质上是被sysmon抢走),和G一起进入休眠状态。如果有此时有一个空闲的M,P绑定在它上面,继续执行其他的G;如果没有空闲的M,但是还有其他的G要执行,那么就会创建一个新的M。参考文章:https://segmentfault.com/a/11...https://blog.csdn.net/weixin_...
