golang 并发--goroutine(四)

golang 语言最大的特点之一就是语法上支持并发,通过简单的语法很容易就能创建一个 go 程,这就使得 golang 天生适合写高并发的程序。这一章节我们就主要介绍 go 程,但是要想完全理解 go 程我们需要深入研究 GPM 模型,关于 GPM 模型网上已经有很多资料了,这里就不过多介绍了,有兴趣的同学可以请教度娘,这一章节还是主要关注应用层面,主要强调在我们写程序时一下需要注意的点并且给出一些代码示例方便我们理解。

通常来说有三种方式实现并发:进程(process),线程(thread),协程(coroutine),前两个:进程和线程是操作系统实现的,协程是由编程语言来实现的,不同语言实现协程的方式不一样,在叫法上也会有一些区别,例如协程在 java 中就叫协程,python 中叫绿色线程(green thread),golang 中叫 go 程(goroutine,因为本文主要是讲解 golang,后续统一用 “go” 程行文)。

进程,线程,go程,是在三个不同粒度实现的并发:

  • 进程

    操作系统实现,拥有独立的内存空间(变量不共享),有进程号数量的限制,由操作系统调度。

  • 线程

    注意这里有两种概念的线程:操作系统线程;CPU 线程。操作系统线程由操作系统实现,是 CPU 执行的最小单元(一核 CPU 同一时刻只能执行一个线程,40 核 CPU 同一时刻能执行 40 个线程),其位于进程之下,一个进程可以有多个线程,但是一个线程只能属于一个进程,多个线程共享进程的内存空间,在操作变量时要考虑安全问题(要加锁防止两个线程同时修改一个变量),线程数受最大线程号限制,由操作系统调度。

  • go 程(协程)

    golang 语言实现,没有数量限制,go程直接共享内存空间,需要考虑并发安全问题,go程的调度时 golang 来完成的,但是同一时刻 CPU 能执行的go程数受线程数限制。

go 程的支持是 golang 的核心优势之一,golang 在语法上支持 go 程(其他语言大都通过第三方现),正是因为有了 go 程,所以 golang 天生适合高并发场景,下面来看一个最简单的 go 程的例子:

package mainimport ("fmt""time"
)func main() {go func() {fmt.Println("Hello, I am a goroutine.")}()fmt.Println("Hello, I am the main goroutine.")time.Sleep(1 * time.Millisecond)
}

输出:

Hello, I am the main goroutine.
Hello, I am a goroutine.

上面代码最后有个 time.Sleep,这是为了防止主 go 程提前退出,大家可以自己测试把 time.Sleep(1 * time.Millisecond) 注释掉,看看输出结果有什么变化。

另外,从上面代码中我们也可以看出 go 程的另外一个与进程,线程不同的地方,go 程的创建没有返回(创建进程和线程在创建成功后会返回进程号或线程号),这也就意味着 golang 程序父 go 程没有方法去操作其子 go 程(完全没有方法也是不对的,通过 golang 的 channel 和 context 可以间接的实现,这个在后续章节再详细讲解)。

GOMAXPROCS

GOMAXPROCS 是 golang 并发种很重要的一个参数,正确的设置这个值对 go 程序的执行效率有很大的影响。要想理解 GOMAXPROCS 我们首先需要搞懂 GPM 模型,这里我只是简单介绍一下(详细的还是要请教度娘):G 表示的就是 goroutine(go 程);P 表示 processer,这是 golang 抽象处理的一个概念,用于调度 goroutine,每个 processer 都有一个 goroutine 队列,队列中的 goroutine 会顺序执行,如果正在执行的 go 程阻塞,processer 会调度后面的 go 程执行;M 表示操作系统线程,processer 最终会把goroutine 交给 M 来执行(但是注意:P 和 M 并不是一一对应的,当 M 因为 IO 发送阻塞时 P 会寻找其他的 M 处理 goroutine,如果没有空闲 M 则创建一个)。GOMAXPROCS 设置的就是 P 的最大数量,他表示同一时刻的 CPU 运行最大 goroutine 数,这个值默认等于 CPU 线程数(通过 lscpu 命令可查)。

设置 GOMAXPROCS 的方法

  • 通过环境变量
export GOMAXPROCS=8

然后通过下面代码验证:

package mainimport ("fmt""runtime"
)func main() {fmt.Printf("GOMAXPROCS=%d\n", runtime.GOMAXPROCS(0))
}

输出结果:

GOMAXPROCS=8
  • 通过代码设置
package mainimport ("fmt""runtime"
)func main() {runtime.GOMAXPROCS(10)fmt.Printf("GOMAXPROCS=%d\n", runtime.GOMAXPROCS(0))
}

输出结果:

GOMAXPROCS=10

注意:这里设置和获取 GOMAXPROCS 使用的都是 runtime.GOMAXPROCS 函数,这个函数参数大于 0 时用于设置 GOMAXPROCS,小于等于 0 时用于获取当前的 GOMAXPROCS。

该如何决定 GOMAXPROCS 的大小

我们知道了 GOMAXPROCS 的设置方法,那么到达设为多少合适呢?这要分为两种情况考虑:

  • 程序运行在物理机上;

    这种情况我们需要考虑两种情形:1、程序是 CPU 密集型(又叫计算密接型)的,GOMAXPROCS 采用默认值(等于物理 CPU 的线程数)就行,如果 GOMAXPROCS 设置过大那么势必会导致多个 P 共用一个 CPU 线程,这样就会因为频繁的调度切换浪费 CPU;2、IO 密集型,这种程序的特点:go 程经常会因为 IO 操作被阻塞,如果 GOMAXPROCS 采用默认值会导致大量 CPU 因为等待 IO 而处于空闲状态,这时候我们就可以设置 GOMAXPROCS 大于 CPU 线程数,根据 IO 操作的密集程度甚至可以远大于 CPU 线程数。

    注意:上面设置的方法都是为了 go 程序充分的使用整个物理机的能力,但是如何如果服务器还需要运行其他服务,并且也需要消耗大量 CPU,这时候我们就需要考虑把 GOMAXPROCS 设置的小一些,防止程序突发占用了整个物理机的 CPU.

  • 程序运行在容器里面,并且容器设置了 resouce limit;

    这种情况我们可以采用 uber 提供的 automaxprocs:https://github.com/uber-go/automaxprocs 由程序自动设置。但是对于第三方程序(没有使用 automaxprocs),我们只能通过环境变量的方式来设置 GOMAXPROCS,但是具体设置为多少,这就只能根据具体容器的 resouce limit 是如何设置的来决定了。

总结:这一章节主要介绍了 GOMAXPROCS,也介绍了该如何设置 GOMAXPROCS,但是上面的场景远远不能覆盖现实情况,生产环境种 GOMAXPROCS 的大小需要综合考虑多种情况。希望这篇文档给大家理解 goroutine 带来一些帮助,如果后续遇到 go 程序性能不理想时可以考 GOMAXPROCS 设置的是否合适。



喜欢的朋友记得点赞、收藏、关注哦!!!

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

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

相关文章

SpringAI人工智能开发框架006---SpringAI多模态接口_编程测试springai多模态接口支持

可以看到springai对多模态的支持. 同样去创建一个项目 也是跟之前的项目一样,修改版本1.0.0 这里 然后修改仓库地址,为springai的地址 然后开始写代码

JSON 系列之1:将 JSON 数据存储在 Oracle 数据库中

本文为Oracle数据库JSON学习系列的第一篇,讲述如何将JSON文档存储到数据库中,包括了版本为19c和23ai的情形。 19c中的JSON 先来看一下数据库版本为19c时的情形。 创建表colortab,其中color列的长度设为4000。若color的长度需要设为32767&a…

SOME/IP 协议详解——信息格式

文章目录 1. 头部格式1.1 消息 ID(Message ID)1.2 长度(Length)1.3 请求 ID(Request ID)1.4 协议版本(Protocol Version):1.5 接口版本(Interface Version&am…

Spring学习(一)——Sping-XML

一、Spring的概述 (一)什么是Spring? Spring是针对bean对象的生命周期进行管理的轻量级容器。提供了功能强大IOC、AOP及Web MVC等功能。Spring框架主要由七部分组成:分别是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 S…

用 gdbserver 调试 arm-linux 上的 AWTK 应用程序

很多嵌入式 linux 开发者都能熟练的使用 gdb/lldb 调试应用程序,但是还有不少朋友在调试开发板上的程序时,仍然在使用原始的 printf。本文介绍一下使用 gdbserver 通过网络调试开发板上的 AWTK 应用程序的方法,供有需要的朋友参考。 1. 下载 …

树莓派换源

查询自己版本: lsb_release -a bullseye可以理解为树莓派的系统代号(10,11,12都不同,一定要看好自己系统是什么版本) 查询架构 uname -a aarch64的地方就是代表系统架构的,我的是aarch64的架…

MySQL索引-索引的结构和原理

索引原理 查找算法 顺序查找 数组链表 二分查找 B树跳表 散列查找 Hash表 DFS 树图 BFS 树图 分块查找 海量数据 Hash结构 Hash索引可以方便的提供等值查询,但是对于范围查询就需要全表扫描了。 Hash索引在MySQL 中Hash结构主要应用在InnoDB 自适应哈希索引。…

【Linux探索学习】第二十三弹——理解文件系统:认识硬件、探索文件在硬件上的存储问题

Linux学习笔记:https://blog.csdn.net/2301_80220607/category_12805278.html?spm1001.2014.3001.5482 前言: 我们前面讲过了文件的组成是由文件内容和文件属性两者组成的,但是我们前面接触的文件都是系统中的文件,都是已经在进…

深度学习中的并行策略概述:2 Data Parallelism

深度学习中的并行策略概述:2 Data Parallelism 数据并行(Data Parallelism)的核心在于将模型的数据处理过程并行化。具体来说,面对大规模数据批次时,将其拆分为较小的子批次,并在多个计算设备上同时进行处…

分布式专题(10)之ShardingSphere分库分表实战指南

一、ShardingSphere产品介绍 Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。Apache ShardingSphere 设计哲学为 Database Plus,旨在…

帧缓存的分配

帧缓存实际上就是一块内存。在 Android 系统中分配与回收帧缓存,使用的是一个叫 ION 的内核模块,App 使用 ioctl 系统调用后,会在内核内存中分配一块符合要求的内存,用户态会拿到一个 fd(有的地方也称之为 handle&…

vue3+vite一个IP对站点名称的前端curd更新-会议系统优化

vue3-tailwind-todo https://github.com/kgrg/vue3-tailwind-todo 基于这个项目,把ip到sta的映射做了前端管理. 核心代码是存储和获得的接口,需要flask提供. def redis2ipdic():global ipdicipdic.clear()tmdiccl.hgetall(IPDIC_KEY)for k in tmdic.keys():ipdic[k.decode() …

Elasticsearch-脚本查询

脚本查询 概念 Scripting是Elasticsearch支持的一种专门用于复杂场景下支持自定义编程的强大的脚本功能,ES支持多种脚本语言,如painless,其语法类似于Java,也有注释、关键字、类型、变量、函数等,其就要相对于其他脚本高出几倍的性…

golang LeetCode 热题 100(动态规划)-更新中

爬楼梯 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢? 示例 1:输入:n 2 输出:2 解释:有两种方法可以爬到楼顶。 1. 1 阶 1 阶 2. 2 阶 示例 2&…

【每日学点鸿蒙知识】Charles抓包、lock文件处理、WebView组件、NFC相关、CallMethod失败等

1、HarmonyOS系统中如何使用Charles抓包? 在HarmonyOS操作系统中,使用Charles进行抓包的步骤如下: 在Charles中设置代理。 首先,在Charles的菜单栏上选择“Proxy”→“Proxy Settings”,然后填入代理端口&#xff0…

抓取手机HCI日志

荣耀手机 1、打开开发者模式 2、开启HCI、ADB调试 3、开启AP LOG 拨号界面输入*##2846579##* 4、蓝牙配对 5、抓取log adb pull /data/log/bt ./

WebAPI编程(第一天,第二天)

WebAPI编程(第一天,第二天) day01 - Web APIs 1.1. Web API介绍 1.1.1 API的概念1.1.2 Web API的概念1.1.3 API 和 Web API 总结 1.2. DOM 介绍 1.2.1 什么是DOM1.2.2. DOM树 1.3. 获取元素 1.3.1. 根据ID获取1.3.2. 根据标签名获取元素1.3.…

windows下Redis的使用

Redis简介: Redis 是一个开源的使用 ANSI C 语言编写、遵守 BSD 协议、支持网络、可基于内存、分布式、可选持久性的键值对(Key-Value)存储数据库,并提供多种语言的 API。 Redis通常被称为数据结构服务器,因为值(value&#xff…

【贪吃蛇小游戏 - JavaIDEA】基于Java实现的贪吃蛇小游戏导入IDEA教程

有问题请留言或私信 步骤 下载项目源码:项目源码 解压项目源码到本地 打开IDEA 左上角:文件 → 新建 → 来自现有源代码的项目 找到解压在本地的项目源代码文件,点击确定 选择“从现有项目创建项目”。点击“下一步” 点击下一步&a…

RTOS下的任务管理

2.3 RTOS下的任务管理(***) RTOS的任务管理主要是进行哪些功能? RTOS的任务管理的多任务管理是怎样进行与实现的? 任务管理中FreeRTOS如何给每个任务分配CPU时间? 文章目录 2.3 RTOS下的任务管理(***)2.3.0 任务概述2.3.1任务的创建与删除2.3…