goroutine简介

golang语言作者Rob Pike说,“Goroutine是一个与其他goroutines 并发运行在同一地址空间的Go函数或方法。一个运行的程序由一个或更多个goroutine组成。它与线程、协程、进程等不同。它是一个goroutine“

  • goroutine通过通道来通信,而协程通过让出和恢复操作来通信;
  • goroutine 通过Golang 的调度器进行调度,而协程通过程序本身调度;

简单的说就是Golang自己实现了协程并叫做goruntine(本文称Go协程),且比协程更强大。

goroutine调度原理

上面说到Go协程是通过Golang的调度器进行调度的,其中调度器的线程模型为两级线程模型。

有关两级线程模型的介绍,可以看这篇文章

我们来看下Golang实现的两级线程模型是怎样的。首先要知道这三个字母代表的含义

  • M:代表内核级的线程
  • P:全程Processor,代表运行Go协程所需要的资源(上下文环境)
  • G:代表Go协程

    我们先看下为实现调度Golang定义了这些数据结构存M,P,G
名称 作用范围 描述
全局M列表 Go的运行时 存放所有M的单向链表
全局P列表 Go的运行时 存放所有P的数组
全局G列表 Go的运行时 存放所有G的切片
调度器的空闲M列表 调度器 存放空闲M的单向链表
调度器的空闲P列表 调度器 存放空闲P的单向链表
调度器的自由G列表 调度器 存放自由G的单向链表(有两个)
调度器的可运行G队列 调度器 存放可运行G的队列
P的自由G列表 本地P 存放当前P中自由G的单向链表
P的可运行G队列 本地P 存放当前P中可运行G的队列

然后从上往下解析Go的两级线程模型图

(1)M和内核线程之间是一对一的关系,一个M在其生命周期中,只会和一个内核线程关联,所以不会出现对内核线程的频繁切换;

Golang的运行时执行系统监控和垃圾回收等任务时候会导致创建M,M空闲时不会被销毁,而是放到一个调度器的空闲M列表中,等待与P关联,M默认数量为10000

(2)P和M之间是多对多的关系,P和G之间是一对多的关系,他们的关联是易变的,由Golang的调度器完成调度;

Golang的运行时按规则调度,让P和不同的M建立或断开关联,使得P中的G能够及时获得运行时机

(3)P的数量默认为CPU总核心数,最大为256,当P没有可运行的G时候(P的可运行G队列为空),P会被放到调度器的空闲P列表中,等待M与它关联;

P有可能会被销毁,如运行时用runtime.GOMAXPROCS把P的数量从32降到16时,剩余16个会被销毁,它们原来的G会先转到调度器可运行的G队列自由G列表

(4)每个P中有可运行的G队列(如图中最下面的那行G)和自由G列表(图中未画出来),当G的代码执行完后,该G不会被销毁,而是被放到P的自由G列表调度器的自由G列表。如果程序新建了Go协程,调度器会在自由G列表中取一个G,然后把Go协程的函数赋值到G中(如果自由G列表为空,就创建一个G);

可见Golang调度器在调度时很大程度复用了M,P,G

(5)在Go程序初始化后,调度器首先进行一轮调度,此时用M去搜索可运行的G。其中我们的main函数也是一个G,找到可运行的G后就执行它;

至于怎么找可运行的G呢?答案是到处找,想尽办法找(这里只列出一部分地方)。

  • 本地P的可运行的G队列
  • 调度器的可运行的G队列
  • 其他P的可运行的G队列

(6)P的可运行G队列最大只能存放长度为256的G,当队列满后,调度器会把一半的G转到调度器的可运行G队列

系统监控

上面大概描述了关于goroutine调度的流程。现在还存在一个问题,那就是当Go协程很多(并发量大)时候,显然G是不能一直执行下去的,因为也需要把执行机会留给其他的G。此时Golang运行时的系统监控就起作用了。
一般情况,当G运行时间超过10ms后,该G就会被系统告知需要停止了,让其他G运行。(这里情况比较复杂,并不能确保每个G都能被公平执行)

以下特殊情况该G不需要停止

  • P的可运行G队列为空(没有其他G可运行)
  • 有空闲的M在寻找可运行的G(没有其他G可运行)
  • 空闲的P(还有P闲着)

总结

Golang以两级线程实现模型,自己实现goruntine和调度器,优势在于并行和非常低的资源使用。

主要体现:

  • 内存消耗方面(每个Go协程占的内存远小于线程占的内存)
  • 切换(调度)开销方面
  • 线程切换涉及模式切换(从用户态切换到内核态)

此外,Go协程执行任务完成的顺序并不都是按我们预期的那样(程序不加以控制的情况下),特别在一些耗时较长的任务中。且每个Go协程执行的时间也不是绝对公平的。

如有错误地方,还请狂喷!

弄懂goroutine调度原理的更多相关文章

  1. go语言之行--golang核武器goroutine调度原理、channel详解

    一.goroutine简介 goroutine是go语言中最为NB的设计,也是其魅力所在,goroutine的本质是协程,是实现并行计算的核心.goroutine使用方式非常的简单,只需使用go关键字 ...

  2. [GO语言的并发之道] Goroutine调度原理&Channel详解

    并发(并行),一直以来都是一个编程语言里的核心主题之一,也是被开发者关注最多的话题:Go语言作为一个出道以来就自带 『高并发』光环的富二代编程语言,它的并发(并行)编程肯定是值得开发者去探究的,而Go ...

  3. 一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  4. 一篇文章彻底弄懂Base64编码原理(转载)

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. Base64的由来 目前Base64已经成为网 ...

  5. 知识扩展——(转)一篇文章彻底弄懂Base64编码原理

    在互联网中的每一刻,你可能都在享受着Base64带来的便捷,但对于Base64的基础原理又了解多少?今天这篇博文带领大家了解一下Base64的底层实现. 一.Base64的由来 目前Base64已经成 ...

  6. 彻底弄懂jQuery事件原理二

    上一篇说到,我们在最外层API的on,off,tiggler,triggerHandler调用的是event方法的add,remove和tirgger方法,本篇就来介绍event辅助类 \ 先放个图, ...

  7. 彻底弄懂jQuery事件原理一

    jQuery为我们提供了一个非常丰富好用的事件API,相对于浏览器自身的事件接口,jQuery有以下特点: 1. 对浏览器进行了兼容性处理,用户使用不需要考虑浏览器兼容性问题 2. 事件数据是保持在内 ...

  8. 图解Go协程调度原理,小白都能理解

    阅读本文仅需五分钟,golang协程调度原理,小白也能看懂,超实用. 什么是协程 对于进程.线程,都是有内核进行调度,有CPU时间片的概念,进行抢占式调度.协程,又称微线程,纤程.英文名Corouti ...

  9. Golang/Go goroutine调度器原理/实现【原】

    Go语言在2016年再次拿下TIBOE年度编程语言称号,这充分证明了Go语言这几年在全世界范围内的受欢迎程度.如果要对世界范围内的gopher发起一次“你究竟喜欢Go的哪一点”的调查,我相信很多Gop ...

随机推荐

  1. Qt基于sqlite数据库的管理小软件

    闲来无事,写了一个基于sqlite的数据库管理小软件. 先上图 中心思想就是: 创建一个数据库 然后每一个分组对应一个数据表 然后遍历该数据表.将名字以treewidgetItem显示出来.添加删除实 ...

  2. tomcat和weblogic发布时,jar包内资源文件的读取路径问题

    问题场景: 本地使用的是tomcat作为发布容器,应用启动后一切正常: 发布测试环境服务器使用weblogic作为发布容器,发布后File类读取文件无法找到文件(路径错误). 问题原因: tomcat ...

  3. java字符串详解

    一.String 类的定义 public final class String implements java.io.Serializable, Comparable<String>, C ...

  4. 前端面试题集锦(一)之HTML部分

    前端的发展日新月异,前端开发也早已从原来的切图套页面,变成了现在的非常复杂的技术体系,近期由于找工作,面试了很多家单位,也总结了一部分前端面试中经常会遇到的面试类型,并一一解答.主要分为HTML.CS ...

  5. JS之null与undefined的区别

    null表示尚未存在的对象 js 代码: alert(null == document.getElementById('notExistElement'));  //output "true ...

  6. 消息中间件——RabbitMQ(三)理解RabbitMQ核心概念和AMQP协议!

    前言 本章学习,我们可以了解到以下知识点: 互联网大厂为什么选择RabbitMQ? RabbiMQ的高性能之道是如何做到的? 什么是AMQP高级协议? AMQP核心概念是什么? RabbitMQ整体架 ...

  7. (18)ASP.NET Core 基于现有数据库创建EF模型(反向工程)

    1.简介 Entity Framework Core可通过数据库提供给应用程序的插件访问许多不同的数据库.我们可以通过使用Entity Framework Core构建执行基本数据访问的ASP.NET ...

  8. Sqlserver 使用.net查询被事务锁住处理

    在有些应用场景中.事务开了后或能需要再去调用某个DAL读取表中的数据.但DAL里又是新开的链接,由于外面有事务所以这里读的时候就读不到.这种情况下可以在这个DAL的查询里开一个新的事务级别设为允许脏读 ...

  9. 章节十六、3-TestNG方法和类注解

    一.Test Suite(测试套件) 我们通常认为一个testcase就是一个测试方法,但是会有很多的testcase,所以我们不可能把所有的testcase放到同一个测试类中,假如需要测试的页面有1 ...

  10. tracert和traceroute介绍

    一.tracert和traceroute简介 相同点:都是用来跟踪路由,帮助排查问题,关注的是过程,而ping关注的是结果 不同点:tracert请求是icmp echo报文 traceroute请求 ...