摘要:它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂。而可以理解为一种语言的协程。线程轻量级进程,,是程序执行流的最小单元。一个标准的线程由线程,当前指令指针,寄存器集合和堆栈组成。其实就是或者等语言中的多线程开发。
grape
全部视频:https://segmentfault.com/a/11...
原视频地址:https://biglive.xueersi.com/L...
GO协程有关知识(扩展)Go语言最大的特色就是从语言层面支持并发(Goroutine),Goroutine是Go中最基本的执行单元。事实上每一个Go程序至少有一个Goroutine:主Goroutine。当程序启动时,它会自动创建。
首先了解什么是协程,什么是线程
协程:又称微线程与子例程(或者称为函数)一样,协程(coroutine)也是一种程序组件。相对子例程而言,协程更为一般和灵活,但在实践中使用没有子例程那样广泛。和线程类似,共享堆,不共享栈,协程的切换一般由程序员在代码中显式控制。它避免了上下文切换的额外耗费,兼顾了多线程的优点,简化了高并发程序的复杂。而Goroutine可以理解为一种Go语言的协程。同时它可以运行在一个或多个线程上。
线程:轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。另外,线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。线程拥有自己独立的栈和共享的堆,共享堆,不共享栈,线程的切换一般也由操作系统调度。
区别:协程是运行在线程上的,在代码中显示控制,没有上下文切换所消耗的资源,协程拥有栈空间来存储自己的上下文,如果栈空间不足,则会更改其他的协程。
Go实现了两种并发形式。第一种是大家普遍认知的:多线程共享内存。其实就是Java或者C++等语言中的多线程开发。另外一种是Go语言特有的,也是Go语言推荐的:CSP(communicating sequential processes)并发模型。
CSP并发模型:以通信的方式来共享内存,传统的多线程之间以共享内存来进行通信,GO语言则以通信的方式来共享内存,那么就来说一说这个CSP模型,它是通过goroutine和channel来实现的。
- goroutine 是Go语言中并发的执行单位。有点抽象,其实就是和传统概念上的”线程“类似,可以理解为”线程“。 - channel是Go语言中各个并发结构体(goroutine)之前的通信机制。 通俗的讲,就是各个goroutine之间通信的”管道“,有点类似于Linux中的管道。
生成一个goroutine需要代码简简单单的go func()就可以生成,channel也是需要声明chan即可,同时,channel在使用时需要注意,在读和写的时候channel都是堵塞的,向管道里写或者读数据类似于流输入输出。
GO并发的实现原理我们先从线程讲起,无论语言层面何种并发模型,到了操作系统层面,一定是以线程的形态存在的。而操作系统根据资源访问权限的不同,体系架构可分为用户空间和内核空间;内核空间主要操作访问CPU资源、I/O资源、内存资源等硬件资源,为上层应用程序提供最基本的基础资源,用户空间呢就是上层应用程序的固定活动空间,用户空间不可以直接访问资源,必须通过“系统调用”、“库函数”或“Shell脚本”来调用内核空间提供的资源。
我们现在的计算机语言,可以狭义的认为是一种“软件”,它们中所谓的“线程”,往往是用户态的线程,和操作系统本身内核态的线程(简称KSE),还是有区别的。也就是说无论你怎么去实现并发,其实根本上都是去调用内核态的线程,一切并发都是纸老虎~
线程模型的实现,可以分为以下几种方式:1.用户级线程模型,2.内核级线程模型,3.两级线程模型。而我们的GO线程模型就是一种特殊的两级线程模型(GPM调度模型)。
GPM调度模型:G: 表示goroutine,存储了goroutine的执行stack信息、goroutine状态以及goroutine的任务函数等;
P: 表示逻辑processor,P的数量决定了系统内最大可并行的G的数量(前提:系统的物理cpu核数>=P的数量);P的最大作用还是其拥有的各种G对象队列、链表、一些cache和状态。
M: M代表着真正的执行计算资源。在绑定有效的p后,进入schedule循环;而schedule循环的机制大致是从各种队列、p的本地队列中获取G,切换到G的执行栈上并执行G的函数,调用goexit做清理工作并回到m,如此反复。M并不保留G状态,这是G可以跨M调度的基础。
P是一个“逻辑Proccessor”,每个G要想真正运行起来,首先需要被分配一个P(进入到P的local runq中,这里暂忽略global runq那个环节)。对于G来说,P就是运行它的“CPU”,可以说:G的眼里只有P。但从Go scheduler视角来看,真正的“CPU”是M,只有将P和M绑定才能让P的runq中G得以真实运行起来。
G-P-M模型的实现算是Go scheduler的一大进步,但Scheduler仍然有一个头疼的问题,那就是不支持抢占式调度,导致一旦某个G中出现死循环或永久循环的代码逻辑,那么G将永久占用分配给它的P和M,位于同一个P中的其他G将得不到调度,出现“饿死”的情况。更为严重的是,当只有一个P时(GOMAXPROCS=1)时,整个Go程序中的其他G都将“饿死”。那怎么办呢?在GO1.2中,他增加了一个监控,Go程序启动时,runtime会去启动一个名为sysmon的m(一般称为监控线程),该m无需绑定p即可运行,该m在整个Go程序的运行过程中至关重要:向长时间运行的G任务发出抢占调度,收回因syscall长时间阻塞的P等等
如果G被阻塞在某个system call操作上,那么不光G会阻塞,执行该G的M也会解绑P(实质是被sysmon抢走了),与G一起进入sleep状态。如果此时有idle的M,则P与其绑定继续执行其他G;如果没有idle M,但仍然有其他G要去执行,那么就会创建一个新M。
参考文章:https://segmentfault.com/a/11...
https://blog.csdn.net/weixin_...
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/31428.html
摘要:原文链接原文作者今天译者注年月日是版本的发布日,同时会对协程的结构化并发做一些介绍。进一步的阅读结构化并发的概念背后有更多的哲学。现代语言开始为我们提供一种以完全非结构化启动并发任务的方式,这玩意该结束了。 原文链接:Structured concurrency 原文作者:Roman Elizarov 今天(译者注:18年9月12日) 是 kotlinx.coroutines 0.26...
摘要:开发负责人创建分支,编写单元测试脚本,编写代码,实现提案中的所有内容,最终发起交叉评审,检查代码,提出改进意见,反馈给开发负责人,继续完善细节。 Swoole开源项目从2012年开始发布第一个版本,到现在已经有近7年的历史。在这七年的时间里: 提交了8821次代码变更 发布了287个版本 收到并解决1161次issue反馈 合并了603次pull request 共有100位开发者...
摘要:为语言提供了强大的协程编程模式。提供的协程语法借鉴自,在此向开发组致敬协程可以与很好地互补。并发执行使用创建协程,可以让和两个函数变成并发执行。协程需要拿到请求的结果。 Swoole4为PHP语言提供了强大的CSP协程编程模式。底层提供了3个关键词,可以方便地实现各类功能。 Swoole4提供的PHP协程语法借鉴自Golang,在此向GO开发组致敬 PHP+Swoole协程可以与...
摘要:关键字表示代码在该处将会被阻塞式暂停阻塞的仅仅是函数代码本身,而不是整个程序,但是这并没有引起函数内部自顶向下代码的丝毫改变。通过实现模式在通过实现理论的过程中已经有一些有趣的探索了。 至此本系列的四篇文章翻译完结,查看完整系列请移步blogs 由于个人能力知识有限,翻译过程中难免有纰漏和错误,望不吝指正issue ES6 Generators: 完整系列 The Basics...
摘要:本周提交的一份增强建议草案要求将虚拟线程作为标准版的一部分进行预览。虚拟线程目的是更好地支持编写和维护高吞吐量并发应用程序。该提案指出,使用虚拟线程不需要学习新的编程模型。我们知道 Go 语言最大亮点之一就是原生支持并发,这得益于 Go 语言的协程机制。一个 go 语句就可以发起一个协程 (goroutin)。 协程本质上是一种用户态线程,它不需要操作系统来进行调度,而是由用户程序自行管理...
阅读 3136·2021-11-11 16:54
阅读 2290·2021-09-04 16:48
阅读 3218·2019-08-29 16:08
阅读 641·2019-08-29 15:13
阅读 1343·2019-08-29 15:09
阅读 2659·2019-08-29 12:45
阅读 1926·2019-08-29 12:12
阅读 444·2019-08-26 18:27