【golang】深入理解GMP调度模型

Goroutine

Go中,协程被称为goroutine,它非常轻量,一个goroutine只占几KB,并且这几KB就足够goroutine运行完,这就能在有限的内存空间内支持大量goroutine,支持了更多的并发,虽然一个goroutine的栈只占几KB(Go语言官方说明为4~5KB),但实际是可伸缩的,如果需要更多内容,runtime会自动为goroutine分配。

Goroutine特点:

  • 占用内存更小(几kb)
  • 调度更灵活(runtime调度)

GMP指的是什么

G(Goroutine):我们在Go语言中所说的协程,为用户级的轻量级线程,每个Goroutine对象中的sched保存着其上下文信息。
M(Machine):对内核级线程的封装,数量对应真实的CPU数(真正干活的对象,默认数量为10000)。
P(Processor):即为G和M的调度对象,用来调度G和M之间的关联关系,其数量可通过,GOMAXPROCS()来设置,默认为核心数。

版本1.0之前GM调度模型

调度器把G都分配M上,不同的G在不同的M并发运行时,都需要向系统申请资源,比如堆栈内存等,因为资源是全局的,就会因为资源竞争造成很多性能损耗。为了解决这样的问题go从1.1版本引入,在运行时系统的时候加入p对象,让P去管理这个G对象,M想要运行G,必须绑定P,才能运行P所管理的G。

在这里插入图片描述

GM调度存在的问题:

  1. 单一全局互斥锁(SchedLock)和集中状态存储
  2. Goroutine传递问题(M经常在M之间传递“可运行”的goroutine)
  3. 每个M作内存缓存,导致内存占用过高,数据局部性较差
  4. 频繁syscall调用,导致严重的线程阻塞/解锁,加剧的性能损耗。

GMP模型

Go中,线程是运行goroutine的实体,调度器的功能是把可运行的goroutine分配到工作线程上。

在这里插入图片描述

  1. 全局队列:存放等待运行的G。
  2. P的本地队列:同全局队列类似,存放的也是等待运行的G,存的数量有限,不超过256个。新建G‘时,G’优先加入到P的本地队列,如果队列满了,则会把本地队列中一半的G移动到全局队列。
  3. P列表:所有的P都在程序启动时创建,并保存在数组中,最多有GOMAXPROCS(可配置)个。
  4. M:线程想运行任务就得获得P,从P的本地队列获取G,P队列为空时,M也会尝试从全局队列拿一批G放到P的本地队列,或从其他P的本地队列偷一半放到自己P的本地队列。M运行G,G执行后,M会从P获取下一个G,不断重复下去。

Goroutine调度器和OS调度器是通过M结合起来的,每个M都代表了1个内核线程,OS调度器负责把内核线程分配到CPU的核上执行。

go func()调度流程

在这里插入图片描述
流程:

  1. 通过go func() 来创建一个goroutine;
  2. 有两个存储G的队列,一个是局部调度器P的本地队列、一个是全局G队列。新创建的G会先保存在P的本地队列中,如果P的本地队列已经满了就会保存在全局的队列中;
  3. G只能运行在M中,一个M必须持有一个P,M与P是1:1的关系。M会从P的本地队列弹出一个可执行状态的G来执行,如果P的本地队列为空,就会想其他的MP组合偷取一个可执行的G来执行。
  4. 一个M调度G执行的过程是一个循环机制;
  5. 当M执行某一个G时候如果发生了syscall或者其余阻塞操作,M会阻塞,如果当前有一些G在执行,runtime会把这个线程M从P中摘除(detach),然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P;
  6. 当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列,如果获取不到P,那么这个线程M变成休眠状态,加入到空闲线程中,然后这个G会被放入全局队列中。

M0
M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量runtime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G,在之后M0就和其他的M一样了。
G0
G0是每次启动一个M都会第一个创建的gourtine,G0仅用于负责调度的G,G0不指向任何可执行的函数,每个M都会有一个自己的G0。在调度或系统调用时会使用G0的栈空间,全局变量的G0是M0的G0

work stealing机制和hand off机制

  • work stealing机制:获取 P 本地队列,当从绑定 P 本地 runq 上找不到可执行的 g,尝试从全局链表中拿,再拿不到从 netpoll 和事件池里拿,最后会从别的 P 里偷任务。
  • hand off机制:当本线程M因为G进行的系统调用阻塞时,线程释放绑定的P,把P转移给其他空闲的M执行。

版本1.2 ~ 1.13 基于协作的抢占式调度

版本1.2~1.13,程序只能依靠Goroutine主动让出CPU资源才能触发调度。

问题:

  • 某些Goroutine可以长时间占用线程,造成其它Goroutine的饥饿
  • 垃圾回收需要暂停整个程序(Stop-the-world,STW),最长可能需要几分钟的时间,导致整个程序无法工作。

版本1.14~至今 基于信号的抢占式调度

在任何情况下,Go 运行时并行执行(注意,不是并发)的 goroutines 数量是小于等于 P 的数量的。为了提高系统的性能,P 的数量肯定不是越小越好,所以官方默认值就是 CPU 的核心数,设置的过小的话,如果一个持有 P 的 M, 由于 P 当前执行的 G 调用了 syscall 而导致 M 被阻塞,那么此时关键点: GO 的调度器是迟钝的,它很可能什么都没做,直到 M 阻塞了相当长时间以后,才会发现有一个 P/M 被 syscall 阻塞了。然后,才会用空闲的 M 来强这个P。通过 sysmon 监控实现的抢占式调度,最快在 20us,最慢在10-20ms才会发现有一个 M 持有 P 并阻塞了。操作系统在 1ms 内可以完成很多次线程调度(一般情况1ms 可以完成几十次线程调度),Go 发起 IO/syscall 的时候执行该 G 的 M会阻塞然后被 OS 调度走,P 什么也不干,sysmon 最慢要10-20ms才能发现这个阻塞,说不定那时候阻塞已经结束了,这样宝贵的 P 资源就这么被阻塞的M 浪费了。

Sysmon 有什么作用?

Sysmon 也叫监控线程,会变动的周期性检查

好处:

  • 释放闲置超过 5 分钟的 span 物理内存;
  • 如果超过 2 分钟没有垃圾回收,强制执行;
  • 将长时间未处理的 netpoll 添加到全局队列;
  • 向长时间运行的 G 任务发出抢占调度(超过10ms的g,会进行retake);
  • 收回因 syscall 长时间阻塞的 P;

GMP调度过程中存在哪些阻塞

  • I/O,select
  • block on syscall(系统调用(syscall)过程中发生了阻塞)
  • channel
  • 等待锁
  • runtime.Gosched()(手动让当前 Goroutine 主动让出执行权)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/110065.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何实现 pdf 转 word

前言:最直接的方式 wps 充会员可以直接转,但是单纯为了 使用这个功能有没啥必要 pdf转word方法 在线转换wps转换其他收费转换方式 在线转换 介绍在线转换,虽然样式简陋但是可以转换成功,转换以后也没有失真 http://ssyr.mynatap…

jeesite实现excel导入功能(保姆级图文教程)

文章目录 前言一、准备工作1.准备一个excel模板,放入static目录2.application.yml文件中设置文件存储路径3.使用easyexcel插件解析excel数据,pom文件导入easyexcel二、使用步骤1.列表页添加下载模板按钮2.表单页添加文件上传3. 创建excel解析对应实体4.后台完成文件上传代码,…

Vivado XADC IP核 使用详解

本文介绍Vivado中XADC Wizard V3.3的使用方法。 XADC简介 XADC Wizard Basic Interface Options: 一共三种,分别是AXI4Lite、DRP、None。勾选后可在界面左侧看到相应通信接口情况。Startup Channel Selection Simultaneous Selection:同时监…

对IP协议概念以及IP地址的概念进行简单整理

网络层重要协议 参考模型和协议栈IP协议IPv4数据报IP数据报格式IPv4地址特殊IP地址私有IP地址和公有IP地址子网划分 参考模型和协议栈 IP协议 IP协议定义了网络层数据传送的基本单元,也制定了一系列关于网络层的规则。 IPv4数据报 网络层的协议数据单元PDU 叫做分…

Webserver项目解析

一.webserver的组成部分 Buffer类 用于存储需要读写的数据 Channel类 存储文件描述符和相应的事件,当发生事件时,调用对应的回调函数 ChannelMap类 Channel数组,用于保存一系列的Channel Dispatcher 监听器,可以设置为epo…

庚顿新一代实时数据库太快了,得用对数坐标轴放大看

不可否认,实时数据处理已经成为企业数据平台的核心能力之一。如何高效处理海量而复杂的实时数据,则取决于数据库系统的架构设计和处理性能。最近,应广大用户的要求,基于飞腾开放实验室Phytium Open Laboratory(POL&…

Golang反射相关知识总结

1. Golang反射概述 Go语言的反射(reflection)是指在运行时动态地获取类型信息和操作对象的能力。在Go语言中,每个值都是一个接口类型,这个接口类型包含了这个值的类型信息和值的数据,因此,通过反射&#x…

JAVA高级技术入门(单元测试,反射,注解,动态代理)

JAVA高级技术入门(单元测试,反射,注解,动态代理) 一、Junit单元测试二、反射1.认识反射,获取类概念:快速入门:获取Class对象的三种方式 2.1获取类的构造器2.2获取类的构造器的作用&a…

【算法专题突破】滑动窗口 - 找到字符串中所有字母异位词(14)

目录 1. 题目解析 2. 算法原理 3. 代码编写 写在最后: 1. 题目解析 题目链接:438. 找到字符串中所有字母异位词 - 力扣(Leetcode) 这道题很好理解,就是找出从不同位置开始的所有异位词。 2. 算法原理 那我们该如…

Android笔记(二十九):利用python自动生成多语言

背景 项目需要支持十几种多语言,而且每个版本的新功能ui都有很多地方需要多语言,如果手动添加非常耗时,于是设计了一个python脚本,通过excel表格转化多语言到项目values/strings文件内 步骤 android工程项目结构 脚本位于langu…

MAUI android连接sqlserver

PDA是android系统,调用金蝶云星空webapi实现仓库收发料,使用读取webapi有些功能无法实现,需要直接读写数据库,读取报错,如图: 用控制台程序测试正常读取 google搜索了一圈, 都需要使用ssl证书才…

pdf添加水印

给pdf文件添加水印 引入依赖 <dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version></dependency>添加水印 package com.it2.pdfdemo02.util;import com.itextpdf.tex…