Go-知识协程

Go-知识协程

  • 1. 基本概念
    • 1.1 进程
    • 1.2 线程
    • 1.3 协程
  • 2. 协程的优势
  • 3. 调度模型
    • 3.1 线程模型
    • 3.2 Go调度器模型
  • 4. 调度策略
    • 4.1 队列轮转
    • 4.2 系统调用
    • 4.3 工作量窃取
    • 4.4 抢占式调度
  • 5. GOMAXPROCS对性能的影响

一个小活动: https://developer.aliyun.com//topic/lingma/activities/202403?taskCode=14508&recordId=40dcecb786f9a65c2e83e95306822ce4#/?utm_content=m_fission_1 「通义灵码 · 体验 AI 编码,开 AI 盲盒」

githubio地址:https://a18792721831.github.io/

1. 基本概念

协程是Go语言最大的特色之一,也是将协程支持的非常优雅的实现,Go在语言层面直接提供对协程的支持。

1.1 进程

进程是应用程序的启动实例,每个进程都有独立的内存空间,不同进程通过进程间的通信方式来通信。

1.2 线程

线程从属于进程,每个进程至少包含一个线程,线程是CPU调度的基本单位,多个线程之间可以共享进程的资源并通过共享内存等线程间的通信方式来通信。

1.3 协程

协程可理解为一种轻量级的线程,与线程相比,协程不受操作系统调度,协程调度器由用户应用程序提供,协程调度器按照调度策略把协程调度到线程中运行。
Go应用程序的协程调度器由runtime包提供,用户使用go关键字即可创建协程,这也就是在语言层面直接支持协程的含义。

2. 协程的优势

在高并发应用中频繁创建线程会造成不必要的开销,所以有了线程池技术。在线程池中预先保存一定数量的线程,新任务将不再以创建线程的方式去执行,
而是将任务发布到任务队列中,线程池中的线程不断地从任务队列中取出任务并执行,这样可以有效地减少线程的创建和销毁带来的开销。
在这里插入图片描述

任务队列中的每一个任务成为G,而G往往代表一个函数。线程池中的worker线程不断地从任务队列中取出任务并执行,而worker现成则交给操作系统进行调度。
如果worker线程执行的G任务中发生系统调用,那么操作系统会将该线程置为阻塞状态,这也就意味着该线程在摸鱼儿,由于消费任务队列中的worker线程变少了,所以线程池消费任务的能力变弱了。
如果任务队列中的大部分任务都进行系统调用,则会让这中间状态恶化,大部分worker线程进入阻塞状态,从而任务队列中的任务产生堆积。
解决这个问题的一个思路就是重新审视线程池中线程的数量,增加线程池中的线程数量可以在一定程度上提高消费能力,但随着线程数量增多,过多线程争抢CPU资源,消费能力会有上限,甚至出现消费能力下降的现象。

一般服从正态分布。

过多的线程会导致上下文切换的开销变大,而工作在用户态的协程则能大大减小上下文切换的开销。协程调度器把可运行的协程逐个调度到线程中执行,
同时及时把阻塞的协程调度出线程,从而有效地避免了线程的频繁切换,达到使用少量线程实现高并发的效果。
多个协程分享操作系统分给线程的时间片,从而达到充分利用CPBU算力的目的,协程调度器则决定了协程运行的顺序。
在这里插入图片描述

3. 调度模型

3.1 线程模型

线程可分为用户线程和内核线程,用户线程由用户创建、同步和销毁,内核线程则由内核来管理。根据用户线程管理方式的不同,分为三种线程模型。

  • N:1 模型:N个用户线程运行在一个内核线程中,优点是用户线程上下文切换快,缺点是无法充分利用多核CPU的算力。
  • 1:1 模型:每个用户线程对应一个内核线程,优点是充分利用CPU的算力,缺点是线程上下文切换较慢。
  • M:N 模型:M个用户线程(协程)运行在N个线程中,优点是充分利用CPU的算力,并且上下文切换快;缺点是该模型的调度算法复杂。

Go语言使用的是M:N模型。

3.2 Go调度器模型

Go协程调度器模型中包含三个关键实体,machine(M),processor§和goroutine(G)。
在这里插入图片描述

  • M(machine): 工作线程,由操作系统调度。
  • P(processor): 处理器(Go定义的一个概念,不是指CPU),包含运行Go代码的必要资源,也有调度goroutine的能力。
  • G(goroutine): Go协程,每个go关键字都会创建一个协程。

M必须持有P才可以执行代码,和系统中的其他线程一样,M也会被系统调度阻塞。P的个数在程序启动时决定,默认情况下等于CPU的核数,可以使用环境变量GOMAXPROCS
或在程序中使用runtime.GOMAXPROCS()方法指定P的个数。
M的个数通常稍大于P的个数,因为除了运行Go代码,runtime包还有其他内置任务需要处理。
在这里插入图片描述

上图中包括两个工作线程M,每个M持有一个处理器P,并且每个M中有一个协程G在运行。其他的协程正在等待被调度,位于被称为runqueues的队列中。
每个处理器P中有一个runqueues队列,此外还有一个全局的runqueues队列,由多个处理器共享。
早期的调度器实现中(Go 1.1 之前)只包含全局的runqueues,多个处理器P通过互斥锁来调度队列中的协程,在多CPU或多核环境中,多个处理器需要经常争抢锁来调度全局队列中的协程,严重影响了并发执行效率。
后来引入了局部的runqueues,每个处理器P访问自己的runqueues时不需要加锁,大大提高了效率。
一般来说,处理器P中的协程G额外在创建的协程会加入本地的runqueues中,但如果本地的队列已满,或者阻塞的协程被唤醒,则协程会被放入全局的runqueues中,处理器P除了调度本地的runqueues中的协程,还会周期性地从全局runqueues中摘取协程来调度。

4. 调度策略

Go 协程调度器也是不断演进的,使得Go支持越来越多的调度策略,以便在不同的应用场景下都能产生优异的并发效果。

4.1 队列轮转

每个处理器P维护者一个协程G的队列,处理器P依次将协程G调度到M中执行。
协程G执行结束后,处理器P会再次调度一个协程G到M中执行。
同时,每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行,全局队列中的G主要来自从系统调用中恢复的G。之所以P会周期性地查看全局队列,也是为了防止全局队列中的G长时间得不到调度机会而长时间等待。

4.2 系统调用

当线程在执行系统调用时,可能会被阻塞,对应到调度器模型,如果一个协程发起系统调度,那么对应的工作线程会被阻塞,这样依赖,处理器P的runqueues队列中的协程将得不到调度,相当于队列中的所有协程都被阻塞。
前面提到P的个数默认等于CPU的核数,每个M必须持有一个P才能执行G。一般情况下M的个数会略大于P的个数,多出来的M将会在G产生系统调用时发挥作用。与线程池类型,Go也有一个M的池子,需要时从池子中获取,用完放回池子,不够用就在创建一个。
在这里插入图片描述

当G0即将进入系统调用时,M0将释放P,进而M1获取P,继续执行P队列中剩下的G。M0由于陷入系统调用而被阻塞,M1接替M0的工作,只要P不空闲,就可以继续使用CPU。
在这里插入图片描述

冗余的M的来源有可能是缓存池,也有可能是新建的。当G0结束系统调用后,根据M0是否能获取到P,对G0进行不同的处理:

  • 如果有空闲的P,则获取一个P,继续执行G0
  • 如果没有空闲的P,则将G0放入全局队列,等待被其他的P调度。然后M0将进入到缓存池睡眠

4.3 工作量窃取

通过go关键字创建的协程通常会优先放到当前协程对应的处理器队列中,可能有些协程自身不断地派生新的协程,而有些协程不派生协程。这样的话,多个处理器P中维护的G队列有可能是不均衡的,如果不加以控制,则有可能出现部分处理器P非常繁忙,而部分处理器P空闲的情况。
为此,Go调度器提供了工作量窃取策略,即当某个处理器P没有需要调度的协程时,将从其他处理器中偷取协程。
在这里插入图片描述

上面的图中,右边的处理器P空闲,当全局队列中也没有协程需要调度,将从左边的处理器的runqueues中偷取一半的协程。
在这里插入图片描述

4.4 抢占式调度

抢占式调度,是指避免某个协程长时间执行,阻碍其他协程被调度。
调度器会监控每个协程的执行时间,一旦执行时间过长且有其他协程在等待执行时,会把写成暂停,转而调度等待的协程,已达到类似于时间片轮转的效果。
在Go 1.14 之前,Go 协程调度器抢占式调度机制有局限性,在设计中,是在函数调用间隙检查协程是否可被抢占,如果协程没有函数调用,则会无限期占用执行权:

package mainimport ("fmt""runtime""time"
)func main() {// 设置只有一个处理器runtime.GOMAXPROCS(1)go func() {for {// 无函数调用的无限循环}}()// 系统调用,让出执行权time.Sleep(1 * time.Second)fmt.Println("xx")
}

运行超时了
在这里插入图片描述

如果在1.14和之后的Go中执行:
在这里插入图片描述

在Go 1.14 中,调度器引入了基于信号的抢占式机制,这个问题才解决了。

5. GOMAXPROCS对性能的影响

程序运行时就将GOMAXPROCS的大小设置为CPU的核数,可让Go程序充分利用CPU。在某些I/O密集型的应用中,这个值可能并不意味着性能最好。
理论上当某个goroutine进入系统调用时,会有一个新的M被启用或创建,继续占用CPU。但由于Go调度器检测到M被阻塞是有一定延迟的,即旧的M被阻塞和新的M得到运行之间是有一定时间间隔的,
所以在I/O密集型应用中可以把GOMAXPROCS的值设置的大一些,或许性能能好一些。

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

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

相关文章

virtualbox 日常运维

前言 虽然平常以macOS和Linux作为主打工作环境,但还是有很多需要用到windows的时候,如camtasia和券商QMT软件。 在二手ThinkPad P53上安装了几个windows虚机,作为测试环境。Mac笔记本远程桌面连接嫌麻烦,还是命令行舒服。MacOS自…

计算机网络—TCP协议详解:特性、应用(1)

🎬慕斯主页:修仙—别有洞天 ♈️今日夜电波:マリンブルーの庭園—ずっと真夜中でいいのに。 0:34━━━━━━️💟──────── 3:34 🔄 ◀️…

Linux文件与进程交互的窥探者lsof

lsof 是一个 Linux 和 UNIX 系统中的实用工具,用于列出系统中打开文件的所有信息。这个名字代表 “List Open Files”,但它也可以显示进程相关的其他信息,如: 打开的文件描述符列表 打开网络连接的列表 被进程使用的信号和内核对象等 在Linux系统中,有一个经典的概念: …

【御控物联】JavaScript JSON结构转换(6):对象To对象——综合应用

文章目录 一、JSON结构转换是什么?二、术语解释三、案例之《JSON对象 To JSON对象》四、代码实现五、在线转换工具六、技术资料 一、JSON结构转换是什么? JSON结构转换指的是将一个JSON对象或JSON数组按照一定规则进行重组、筛选、映射或转换&#xff0…

java(4)之运算符

1、算术运算符 运算符含义表达式加11-减1-1*乘1*2/除2/1%取余5%2 2、赋值运算符 即 表示将右边的值赋给左边的变量 即 int i ; i 1; 运算符含义 表达式 x xyxy-x x-yx - y*x x*yx*y/x x/yx /y%x x%yx %y 代码示例 public class Main {pub…

DXP学习3-单片机时钟显示系统的层次原理图设计

目录 一,自上而下的子母图设计 1,绘制层次式电路母图 1)工程及原理图创建和保存 2)开始绘制层次式母图main.SchDoc 2,绘制图纸符号 1)properties选项卡 2)designator标号 3)filename文件名 4&…

Kafka、ActiveMQ、RabbitMQ和RocketMQ都有哪些区别?

一、问题解析 Kafka、ActiveMQ、RabbitMQ和RocketMQ都是常见的消息中间件,它们都提供了高性能、高可用、可扩展的消息传递机制,但它们之间也有以下一些区别: 1、消息传递模型:Kafka主要支持发布-订阅模型,ActiveMQ、R…

什么是人工智能物联网(AIoT)?

过去十年,从医疗设备、家庭和楼宇自动化到工业自动化等各个领域,物联网 (IoT) 设备的数量呈爆炸式增长。设备包括可穿戴设备、传感器、电器和医疗监视器——所有这些设备都相互连接,收集和共享大量数据。国际数据公司 (IDC) 预测,…

蓝桥杯c++递归与递推

数字三角形 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm>using namespace std; const int N 110; int map[N][N]; int n;int main() {cin >> n;for(int i0;i<n;i){for(int j0;j<i;j){cin >> map[i]…

做好自动化测试必备的5大技能,懂一个就超过了99%的人

或许还有一些人认为软件测试的门槛很低&#xff0c;低到任何人都可以做&#xff0c;随便点点就可以了。这里需要澄清一下&#xff0c;不管哪一类测试岗位&#xff0c;如果做深做精都需要下功夫&#xff0c;只是精通的方向不同。试问一下如果让一个什么都不懂、一点业务基础都没…

Linux之ssh服务

目录 一、ssh简介 ssh组件 二、配置文件 三、相关的命令 ssh scp 四、密钥认证 一、ssh简介 远程登陆linux用的就是ssh服务 ssh服务特点就是数据会机密传输 ssh组件 组件&#xff1a;openssh 服务器&#xff1a;sshd 默认端口&#xff1a;22 二、配置文件 /etc/ssh/ss…

【题目】【网络系统管理】2021年全国职业院校技能大赛模块B--样题(三)

2021年全国职业院校技能大赛 网络系统管理&#xff08;样题3&#xff09;模块B&#xff1a;Windows环境 全国职业院校技能大赛执委会.技术专家组 2021年03月 目录 一、赛题说明 3 &#xff08;一&#xff09;竞赛介绍 3 &#xff08;二&#xff09;密码 3 &#xff08;三…

理解 SQL 数据添加:从基础到实践

引言&#xff1a; 在现代软件开发中&#xff0c;数据库是不可或缺的一部分。而 SQL 作为结构化查询语言的代表&#xff0c;广泛应用于数据库管理系统中&#xff0c;为我们提供了强大的数据管理和查询能力。 主题&#xff1a; 我们将从基础的 SQL INSERT INTO 语句开始&…

【智能家居项目】RT-Thread版本——DHT11获取温湿度 | MQTT上传到服务器 | 服务器控制外设

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《智能家居项目》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 这篇文章中&#xff0c;本喵将使用RT-Thread Studio来实现这个智能家居的项目&#xff0c;最终…

【智能算法】帝企鹅优化算法(EPO)原理及实现

目录 1.背景2.算法原理2.1算法思想2.2算法过程 3.结果展示4.参考文献 1.背景 2018年&#xff0c;G Dhiman等人受到自然界中帝企鹅群体社会行为启发&#xff0c;提出了帝企鹅优化算法&#xff08;Emperor Penguin Optimizer, EPO&#xff09;。 2.算法原理 2.1算法思想 EPO模…

每日OJ题_队列_宽搜bfs④_力扣515. 在每个树行中找最大值

目录 力扣515. 在每个树行中找最大值 解析代码 力扣515. 在每个树行中找最大值 515. 在每个树行中找最大值 难度 中等 给定一棵二叉树的根节点 root &#xff0c;请找出该二叉树中每一层的最大值。 示例1&#xff1a; 输入: root [1,3,2,5,3,null,9] 输出: [1,3,9]示例2…

AI 时代来临我们要如何面对?

人工智能&#xff08;AI&#xff09;已经引领了人类社会进入了全新的时代&#xff0c;开启了科技进步的崭新篇章。在过去几十年&#xff0c;人工智能技术迅猛发展&#xff0c;并已经逐步融入到我们的日常生活和职场中。 随着科技的不断进步&#xff0c;人工智能已成为社会发展…

渐变颜色作图

clear clc close all % 生成 x 值 x linspace(0, 5, 1000); % 计算对应的 y 值&#xff08;二次函数分布&#xff09; y x .^ 2; % 添加一些随机噪声 y y randn(size(y)); clinspace(1,10,length(x)); arry1[x,y]; arry2sortrows(arry1,2,descend); arry3[arry2,c]…

求组合背包II(acwing)

题目描述&#xff1a; 给定n组循问&#xff0c;每组询问给定两个整数a&#xff0c;b&#xff0c;请你输出Ca^b mod (1e9 7)的值&#xff0c;。 输入格式&#xff1a; 第一行包含整数n。 接下来2行&#xff0c;每行包含一组a和b。 输出格式&#xff1a; …

类的函数成员(三):拷贝构造函数

一.什么是拷贝构造函数&#xff1f; 1.1 概念 同一个类的对象在内存中有完全相同的结构&#xff0c;如果作为一个整体进行复制或称拷贝是完全可行的。这个拷贝过程只需要拷贝数据成员&#xff0c;而函数成员是共用的&#xff08;只有一份拷贝&#xff09;。 在建立对象…