Go--协程

协程

协程是Go语言最大的特色之一。

1、协程的概念

协程并不是Go发明的概念,支持协程的变成语言有很多。Go在语言层面直接提供对协程的支持称为goroutine。

1.1 基本概念
  1. 进程

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

  2. 线程

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

  3. 协程

    协程可以理解为一种轻量级线程,与线程相比,协程不受操作系统调度,协程调度器由用户应用程序提供,协程调度器按照调度策略把协程调度到线程中运行。Go应用程序得到协程调度器由runtime包提供,用户使用go关键字即可创建协程。

1.2 协程的优势

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

​ 下图展示了一个典型的线程池:
在这里插入图片描述

​ 我们把任务队列中的每个任务称作G,而G往往代表一个函数。线程池中的worker线程不断地从任务队列中取出任务并执行,而worker线程则交给操作系统进行调度。

​ 如果worker线程执行的G任务中发生系统调用,则操控系统会将线程置为阻塞状态,也就意味着该线程在怠工,由于消费任务队列中的worker线程变少了,所以线程池消费任务队列的能力变弱了。

​ 如果任务队列中的大部分任务都进行系统调用,则会让这种状态恶化,大部分worker线程进入阻塞状态,从而任务队列中的任务产生堆积。

​ 解决这个问题的一个思路是重新审视线程池中线程的数量,增加线程池中的线程数量,以在一定程度上提高消费力,但随着线程数量增多,过多线程争抢CPU资源,消费能力会有上限,甚至出现消费能力下降的现象,如下图所示。

在这里插入图片描述

​ 过多的线程会导致上下文切换的开销变大,而工作在用户态的协程能大大减少上下文切换的开销。协程调度器把可运行的协程逐个调度到线程中执行,同时及时把阻塞的协程调度出协程,从而有效地避免了线程的频繁切换,达到了使用少量线程实现高并发地效果。

​ 多个协程分享操作系统分给线程的时间片,从而达到充分利用CPU算力的目的,协程调度器则决定了协程执行的顺序。

2、调度模式
2.1 线程模型

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

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

​ Go协程调度模型中包含三个关键实体,machine(简称M)、processor(简称P)和 goroutine (简称G)。

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

​ M必须持有P才可以执行代码,跟系统中的其他线程一样,M也会被系统调用阻塞。P的个数在程序启动时决定,默认情况下等同于CPU的核数,可以使用环境变量 GOMAXPROCS 或在程序中使用runtime.GOMAXPROCS()犯法指定P的个数。

​ M的个数通常稍大于P的个数,因为除了运行Go代码,runtime包还有其他内置任务需要处理。一个简单的调度器模型如下图所示。

在这里插入图片描述

​ 上图中包括两个工作线程M,每个M持有一个处理器P,并且每个M中有一个绿色背景的协程G在运行。其余的协程正在等待被调用,它们位于被称为runqueues的队列中。每个处理器P中拥有一个runqueues队列,此外还有一个全局的runqueues队列,由多个处理器共享。

​ 早期的调度器实现中(Go1.1之前)只包含全局的runqueues,多个处理器P通过互斥锁来调度队列中的线程,在多个CPU或多核环境中,多个处理器需要经常争抢锁来调度全局队列中的协程,严重影响了并发执行效率。后来便引入了局部runqueues,每个处理器P访问自己的runqueues时不需要加锁,大大提高了效率。

​ 一般来说,处理器P中的协程G额外再创建的协程会加入本地的runqueues中,但如果本地的队列已满,或者阻塞的协程被唤醒,则协程会被放入全局的runqueues中,处理器P除了调度本地的runqueues中的协程,还会周期性地从全局runqueues中摘取协程来调度。

3、调度策略
3.1 队列轮转

​ 每个处理器P维护着一个协程G的队列,处理器P依次将协程G调度到M中执行。同时每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行,全局队列中的G主要来自从系统调用中恢复的G。

3.2 系统调用

​ 当线程在执行系统调用时,可能会阻塞,对应到调度器模型,如果一个协程发起系统调用,那么对应的工作线程会被阻塞,这样一来,处理器P的runqueues队列中的协程将得不到调用,相对于队列中的所有协程都被阻塞

​ 前面提到P的个数默认等于CPU的核数,每个M必须持有一个P才可以执行G。一般情况下M的个数略大于P的个数,多出来的M将会在G产生系统调用时发挥作用。与线程池类似,Go也提供一个M的池子,需要时从池子中获取,用完放回池子,不够时就再创建一个。

​ 当M运行的某个G产生系统调用时,过程如下图所示。

在这里插入图片描述

​ 当Go即将进入系统调用时,M0将释放P,进而某个冗余的M1获取P,继续执行P队列中剩下的G。M0由于陷入系统调用而被阻塞,M1接管M0的工作,只要P不空闲,就可以保证充分利用CPU。

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

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

​ 通过go关键字创建的协程通常会优先放到当前协程对应的处理器队列中,可能有些协程自身不断地派生出新的协程,而有些协程不派生协程。如此一来,多个处理器P中维护地G队列有可能是不均衡的,如果不加以控制,则有可能出现部分处理器P非常繁忙,而部分处理器怠工的情况。

​ 为此,Go调度器提供了工作量切取策略,当某个处理器P没有需要调度的协程时,将从其他处理器中切取协程。

在这里插入图片描述

​ 发生切取前右侧的处理器P在没有协程需要调度时会查询全局队列,如果全局队列中也没有协程需要调度,则会从另一个正在运行的处理器P中偷取协程,每次偷取一半。

3.4 抢占式调度

​ 调度器会监控每个协程的执行时间,一旦执行时间过长且有其他协程在等待时,会把协程暂停,转而调度等待的协程,以达到类似于时间片轮转的效果。

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

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

相关文章

nodejs微信小程序+python+PHP的智能停车系统-计算机毕业设计推荐django

目 录 摘 要 I ABSTRACT II 目 录 II 第1章 绪论 1 1.1背景及意义 1 1.2 国内外研究概况 1 1.3 研究的内容 1 第2章 相关技术 3 2.1 nodejs简介 4 2.2 express框架介绍 6 2.4 MySQL数据库 4 第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:…

Glide系列-活动缓存和内存缓存

1.活动缓存数据结构用的HashMap final class ActiveResources {VisibleForTesting final Map<Key, ResourceWeakReference> activeEngineResources new HashMap<>(); } 2.内存缓存LinkedHashMap public class LruCache<T, Y> {private final…

解决思维题的一些自我总结

目录 常见思维题类型 排序 区间问题 01串串 字符串串 位运算 gcd 与 lcm 质数相关 二元组 常见思维题类型 思维题很多都可以说是贪心、但贪心种类很多&#xff0c;具体怎么贪&#xff0c;重要的还是在于积累经验吧...有些东西也很难总结&#xff0c;以下算是我的碎碎念…

Next.js 中的中间件

Next.js 中的中间件 Next.js 中的中间件是一个功能强大的工具&#xff0c;允许开发人员拦截、修改和控制应用程序中的请求和响应流。无论我们是构建服务器渲染的网站还是成熟的 Web 应用程序&#xff0c;了解如何有效使用中间件都可以显着增强项目进出的数据流。本文将从基础知…

Thymeleaf生成pdf表格合并单元格描边不显示

生成pdf后左侧第一列的右描边不显示&#xff0c;但是html显示正常 显示异常时描边的写法 cellpadding“0” cellspacing“0” &#xff0c;td,th描边 .self-table{border:1px solid #000;border-collapse: collapse;width:100%}.self-table th{font-size:12px;border:1px sol…

el-select的多选multible带全选组件二次封装(vue2,elementUI)

1.需求 Select 选择器 多选需要增加 全选 和 取消全选 功能&#xff0c;前端框架为vue2&#xff0c;UI组件为elementUI。 2. 代码 html部分 <template><el-tooltip effect"dark" :disabled"defaultValue.length < 0" :content"defaul…

GO设计模式——7、适配器模式(结构型)

目录 适配器模式&#xff08;Adapter Pattern&#xff09; 优缺点 使用场景 注意事项 代码实现 适配器模式&#xff08;Adapter Pattern&#xff09; 适配器模式&#xff08;Adapter Pattern&#xff09;是作为两个不兼容的接口之间的桥梁。将一个类的接口转化为客户希望的…

dockerfile简单实践部署(jenkins,wordpress)

实现部署jenkins的流程 配置java环境&#xff0c;导入jenkins包&#xff0c;运行命令 java -jar jenkins包&#xff0c;这里为了减少进入jenkins的web端安装插件&#xff0c;将插件提前部署到容器内。 制作dockerfile 创建镜像所在的文件夹和Dockerfile文件 mkdir /test cd …

如何优雅使用 vue-html2pdf 插件生成pdf报表

使用 vue-html2pdf 插件 业务背景&#xff0c;老板想要一份能征服客户的pdf报表&#xff0c;传统的pdf要手撕&#xff0c;企业中确实有点耗费时间&#xff0c;于是github上面看到开源的这个插件就…废话不多说&#xff0c;直接上教程 1.使用下面命令安装 vue-html2pdf npm i…

Vue3项目调用腾讯地图服务(地址解析 地址转坐标)及使用axios的跨域问题

一,需求 根据传入的文本地址 将其转换为坐标 显示地图点位在腾讯地图上 二,使用axios发送请求 import axios from axios; //引入axiosaxios({url:https://apis.map.qq.com/ws/geocoder/v1,method:get//参数 地址和key值}).then((data)>{console.log(data)});但是使用完报跨…

第二十一章总结博客

网络程序设计基础 局域网与互联网 为了实现两台计算机的通信&#xff0c;必须用一个网络线路连接两台计算机。如下图所示 网络协议 1.IP协议 IP是Internet Protocol的简称&#xff0c;是一种网络协议。Internet 网络采用的协议是TCP/IP协议&#xff0c;其全称是Transmission …

阿里云 ACR 制品中心 AI/大数据镜像专场上新推荐榜

今天&#xff0c;AI 领域的快速发展不仅需要算法的突破&#xff0c;也需要工程的创新。随着容器技术和服务在企业的应用程度不断加深&#xff0c;企业对于容器的使用也越来越多地从在线业务逐渐向 AI、大数据类型的工作负载发展。同时&#xff0c;开发人员在考虑如何通过云原生…

MQTT源码分析

目录 MQTT源码分析 1. MQTT客户端功能 2. 客户端软件如何实现 3. 程序分层 4. 情景分析 4.1 连接服务器 4.2 创建线程 4.3 发布消息 4.4 最复杂&#xff1a;订阅消息 MQTT源码分析 分析源码&#xff1a;mqttclient\test\emqx\test.c 参考资料&#xff1a; kawaii-mqt…

IntelliJ IDEA 2023.3 最新变化

关键亮点 AI Assistant 预览阶段结束 全面推出 Ultimate JetBrains AI Assistant 现已全面推出&#xff0c;搭载大量新功能和改进&#xff0c;助力提高您在 JetBrains IDE 中的工作效率。 最新更新包括编辑器中增强的直接代码生成、无需复制代码即可回答项目相关查询的上下文…

Linux篇:进程间通信

一、进程间通信原理&#xff1a; 1、通信是有成本的&#xff1a;两个或者多个进程&#xff0c;实现数据层面的交互&#xff0c;因为进程独立性的存在&#xff0c;导致进程通信的成本比较高。 2、进程间通信的方式&#xff1a; ①基本数据 ②发送命令 ③某种协同 ④通知 .....…

Hibernate更新多实体对象的坑

目录 Hibernate中的脏检查机制 多线程环境下的问题 解决方案 1. 使用乐观锁 2. 使用悲观锁 3. 使用同步机制 总结与建议 在Hibernate中&#xff0c;当一个大对象&#xff08;通常是一个实体对象&#xff09;包含了几个小对象&#xff08;通常是关联的实体对象&#xff0…

【Python】np.maximum()和np.minimum()函数详解和示例

本文通过函数原理和运行示例&#xff0c;对np.maximum()和np.minimum()函数进行详解&#xff0c;以帮助大家理解和使用。 更多Numpy函数详解和示例&#xff0c;可参考 【Python】Numpy库近50个常用函数详解和示例&#xff0c;可作为工具手册使用 目录 np.maximum()函数解析运…

华为OD机试 - 攀登者2(Java JS Python C)

题目描述 攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。 地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。 例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的地图,地图中有两个山脉位置分别为 1,2,3,4,5…

我有才打造知识付费小程序

一站式线上线下活动管理 为用户提供“精彩城市生活和人脉资源”。 在线活动提供创业、互联网、科技、投资、金融、教育、亲子、生活、聚会交友、医疗、设计、分享会、脱口秀、音乐演出等多种活动类型, 为职场白领提升技能、拓展人脉、聚会交友的首选平台。 为主办方提供“一…

Navicat 连接 GaussDB分布式的快速入门

Navicat Premium&#xff08;16.3.3 Windows版或以上&#xff09;正式支持 GaussDB 分布式数据库。GaussDB分布式模式更适合对系统可用性和数据处理能力要求较高的场景。Navicat 工具不仅提供可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构…