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. 用HTML5的canvas做一个时钟

    对于H5来说,canvas可以说是它最有特色的一个地方了,有了它之后我们可以随意的在网页上画各种各样的图形,做一些小游戏啊什么的.canvas这个标签的用法,在网上也有特别多的教程了,这里就不作介绍了 ...

  2. java 各基本类型转 bytes 数组

    java 将 基本类型转byte[] 数组时,需考虑大端小端问题 1. 大端格式下,基本类型与byte[]互转 BigByteUtil.java package com.ysq.util; impor ...

  3. macos Mojave 出现网络出错 Frame Check Sequence: Bad checksum

    问题描述:使用软电话外呼的时候出现Request Timeout . 端口监听之后通过 Wireshark发现错误:`Frame Check Sequence: Bad checksum`,查看wir ...

  4. postman->newman->jenkins构建过程的问题记录及解决方法

    从postman导出请求集合后要做的工作: 需要调整导出的json文件,如配置环境变量{{host}},需要修改成准确的url; 通过newman执行newman run test_request.j ...

  5. Spring项目集成ShiroFilter简单实现权限管理

    Shiros是我们开发中常用的用来实现权限控制的一种工具包,它主要有认证.授权.加密.会话管理.与Web集成.缓存等功能.我是从事javaweb工作的,我就经常遇到需要实现权限控制的项目,之前我们都是 ...

  6. Linux--shell编程原理--03

    一.编程原理: 1.编程介绍: 计算机只能识别二进制指令 程序=指令+数据 根据服务的重心不同,我们分为面向过程编程,面向对象编程: a) 面向过程:侧重于指令的编程语言 b) 面向对象:侧重于数据的 ...

  7. Gin + Vue全栈开发实战(二)

    尝试地写了第一篇自己学习Go Web框架的感受和入门的文章,发现反响还不错,大家也提出了很多的问题来一起交流.近期也渐渐地出现了很多有关go语言开发的相关文章,包括有在蚂蚁金服的大牛的分享,我也一直有 ...

  8. 服务注册组件——Eureka高可用集群搭建

    服务注册组件--Eureka高可用集群搭建 什么是Eureka? 服务注册组件:将微服务注册到Eureka中. 为什么需要服务注册? 微服务开发重点在一个"微"字,大型应用拆分成微 ...

  9. linux细节操作的

    一>安装mysql 可以直接在linux系统下载 下载之前要安装wget插件 下载命令 wget 后面跟安装软件的url 比如mysql wget http://repo.mysql.com/m ...

  10. vulnhub-XXE靶机渗透

    下载链接:https://download.vulnhub.com/xxe/XXE.zip   0x01 确定目标IP 目标网络模式默认为net模式,无需修改 使用arp-scan或者netdisco ...