当前位置: 首页 > news >正文

一文读懂什么是Go语言goroutine

1. 进程、线程和协程的区别

  • 进程: 进程是具有一定独立功能的程序,进程是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。

  • 线程: 线程是进程的一个实体,线程是内核态,而且是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。

  • 协程: 协程是一种用户态的轻量级线程,协程的调度完全是由用户来控制的。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

协程,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态中执行)。

  • 线程和协程的区别

    1. 线程切换需要陷入内核,然后进行上下文切换,而协程在用户态由协程调度器完成,不需要陷入内核,这样代价就小了。
    2. 协程的切换时间点是由调度器决定,而不是由系统内核决定的,尽管它们的切换点都是时间片超过一定阈值,或者是进入 I/O 或睡眠等状态时。
    3. 基于垃圾回收的考虑,Go 实现了垃圾回收,但垃圾回收的必要条件是内存位于一致状态,因此就需要暂停所有的线程。如果交给系统去做,那么会暂停所有的线程使其一致。对于 Go 语言来说,调度器知道什么时候内存位于一致状态,所以也就没有必要暂停所有运行的线程。

2. 介绍一下 Goroutine

Goroutine 是一个与其他 goroutines 并行运行在同一地址空间的 Go 函数或方法。

协程(goroutine) 轻量级线程, goroutine 是由 Go 的运行时(runtime)调度和管理的。Go 程序会智能地将 goroutine 中的任务合理地分配给每个 CPU。它在语言层面已经内置了调度和上下文切换的机制。

goroutine 是 Go 并发设计的核心,也叫协程,它比线程更加轻量,因此可以同时运行成千上万个并发任务。在 Go 语言中,每一个并发的执行单元叫作一个 goroutine。我们只需要在调用的函数前面添加 go 关键字,就能使这个函数以协程的方式运行。

3. context 包结构原理和用途

Context(上下文)是 Golang 应用开发常用的并发控制技术 ,它可以控制一组呈树状结构的 goroutine,每个 goroutine 拥有相同的上下文。Context 是并发安全的,主要是用于控制多个协程之间的协作、取消操作。

Context 只定义了接口,凡是实现该接口的类都可称为是一种 context。contex理分析

  • 「Deadline」 方法:可以获取设置的截止时间,返回值 deadline 是截止时间,到了这个时间,Context 会自动发起取消请求,返回值 ok 表示是否设置了截止时间。
  • 「Done」 方法:返回一个只读的 channel ,类型为 struct {}。如果这个 chan 可以读取,说明已经发出了取消信号,可以做清理操作,然后退出协程,释放资源。
  • 「Err」 方法:返回 Context 被取消的原因。
  • 「Value」 方法:获取 Context 上绑定的值,是一个键值对,通过 key 来获取对应的值。

4. goroutine 调度

GMP 是 Go 语言运行时(runtime)层面的实现,是 go 语言自己实现的一套调度系统。区别于操作系统调度 OS 线程。

  1. G 很好理解,就是个 goroutine 的,里面除了存放本 goroutine 信息外 还有与所在 P 的绑定等信息。
  2. P 管理着一组 goroutine 队列,P 里面会存储当前 goroutine 运行的上下文环境(函数指针,堆栈地址及地址边界),P 会对自己管理的 goroutine 队列做一些调度(比如把占用 CPU 时间较长的 goroutine 暂停、运行后续的 goroutine 等等)当自己的队列消费完了就去全局队列里取,如果全局队列里也消费完了会去其他 P 的队列里抢任务。
  3. M(machine)是 Go 运行时(runtime)对操作系统内核线程的虚拟, M 与内核线程一般是一一映射的关系, 一个 groutine 最终是要放到 M 上执行的;

    P 与 M 一般也是一一对应的。他们关系是: P 管理着一组 G 挂载在 M 上运行。当一个 G 长久阻塞在一个 M 上时,runtime 会新建一个 M,阻塞 G 所在的 P 会把其他的 G 挂载在新建的 M 上。当旧的 G 阻塞完成或者认为其已经死掉时 回收旧的 M。

    P 的个数是通过 runtime.GOMAXPROCS 设定(最大 256),Go1.5 版本之后默认为物理线程数。 在并发量大的时候会增加一些 P 和 M,但不会太多,切换太频繁的话得不偿失。

    单从线程调度讲,Go 语言相比起其他语言的优势在于 OS 线程是由 OS 内核来调度的,goroutine 则是由 Go 运行时(runtime)自己的调度器调度的,这个调度器使用一个称为 m:n 调度的技术(复用 / 调度 m 个 goroutine 到 n 个 OS 线程)。 其一大特点是 goroutine 的调度是在用户态下完成的, 不涉及内核态与用户态之间的频繁切换,包括内存的分配与释放,都是在用户态维护着一块大的内存池, 不直接调用系统的 malloc 函数(除非内存池需要改变),成本比调度 OS 线程低很多。 另一方面充分利用了多核的硬件资源,近似的把若干 goroutine 均分在物理线程上, 再加上本身 goroutine 的超轻量,以上种种保证了 go 调度方面的性能。

5. 如何避免 Goroutine 泄露和泄露场景

gorouinte 里有关于 channel 的操作,如果没有正确处理 channel 的读取,会导致 channel 一直阻塞住,goroutine 不能正常结束

  • channel 操作不当:
package mainimport ("fmt""time"
)func main() {// 问题场景: 在 Goroutine 中创建 channel,但没有相应的 channel 读取操作go func() {ch := make(chan int)ch <- 1 // 这里会导致 Goroutine 永久阻塞}()time.Sleep(5 * time.Second)fmt.Println("Program ended")
}

在上面的代码中,我们创建了一个 Goroutine,它向一个无人接收的 channel 写入数据。这会导致该 Goroutine 永久阻塞,从而造成 Goroutine 泄露。

解决方法如下:

package mainimport ("fmt""time"
)func main() {// 解决方法: 确保 channel 的读取操作与写入操作相匹配ch := make(chan int)go func() {x := <-ch // 从 channel 中读取数据fmt.Println("Received value:", x)}()ch <- 1 // 向 channel 中写入数据time.Sleep(5 * time.Second)fmt.Println("Program ended")
}

在这个解决方案中,我们在创建 Goroutine 之前先创建了 channel,并在 Goroutine 中读取 channel 中的数据。这样可以确保 Goroutine 能够正常退出,避免泄露。

  • context 使用不当:
package mainimport ("context""fmt""time"
)func main() {// 错误示例: 在 Goroutine 中使用 context,但没有正确地取消 contextctx, _ := context.WithTimeout(context.Background(), 2*time.Second)go func() {doSomething(ctx)}()time.Sleep(5 * time.Second)fmt.Println("Program ended")
}func doSomething(ctx context.Context) {for {// 不检查 ctx.Done() 就一直执行time.Sleep(time.Second)fmt.Println("Doing something...")}
}

在上面的代码中,我们创建了一个 Goroutine,并向其传递了一个 context。但是,我们没有在 doSomething 函数中正确地处理 context 的取消。这意味着,即使 context 已经超时,Goroutine 仍然会一直执行下去,而不会退出。,从而造成 Goroutine 泄露。

解决方法如下:

package mainimport ("context""fmt""time"
)func main() {// 解决方法: 在 Goroutine 中正确地处理 context 的取消ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)defer cancel()go func() {doSomething(ctx)}()time.Sleep(5 * time.Second)fmt.Println("Program ended")
}func doSomething(ctx context.Context) {for {select {case <-ctx.Done():fmt.Println("Context canceled, exiting Goroutine")returndefault:// 执行一些操作time.Sleep(time.Second)fmt.Println("Doing something...")}}
}

在解决方案中,我们在 doSomething 函数中使用 select 语句来检查 context 是否已被取消。如果 context 已被取消,Goroutine 就会安全地退出,避免 Goroutine 泄露。

6. waitgroup 用法和原理

waitgroup 内部维护了一个计数器,当调用 wg.Add(1) 方法时,就会增加对应的数量;当调用 wg.Done() 时,计数器就会减一。直到计数器的数量减到 0 时,就会调用
runtime_Semrelease 唤起之前因为 wg.Wait() 而阻塞住的 goroutine。

使用方法:

  1. main 协程通过调用 wg.Add (delta int) 设置 worker 协程的个数,然后创建 worker 协程;
  2. worker 协程执行结束以后,都要调用 wg.Done ();
  3. main 协程调用 wg.Wait () 且被 block,直到所有 worker 协程全部执行结束后返回。

    实现原理:

    • WaitGroup 主要维护了 2 个计数器,一个是请求计数器 v,一个是等待计数器 w,二者组成一个 64bit 的值,请求计数器占高 32bit,等待计数器占低 32bit。

    • 每次 Add 执行,请求计数器 v 加 1,Done 方法执行,请求计数器减 1,v 为 0 时通过信号量唤醒 Wait ()。

相关文章:

一文读懂什么是Go语言goroutine

1. 进程、线程和协程的区别 进程: 进程是具有一定独立功能的程序&#xff0c;进程是系统资源分配和调度的最小单位。每个进程都有自己的独立内存空间&#xff0c;不同进程通过进程间通信来通信。由于进程比较重量&#xff0c;占据独立的内存&#xff0c;所以上下文进程间的切换…...

计算机毕业设计 农家乐管理平台 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

&#x1f34a;作者&#xff1a;计算机编程-吉哥 &#x1f34a;简介&#xff1a;专业从事JavaWeb程序开发&#xff0c;微信小程序开发&#xff0c;定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事&#xff0c;生活就是快乐的。 &#x1f34a;心愿&#xff1a;点…...

Spring Boot优缺点

Spring Boot 是一款用于简化Spring应用开发的框架&#xff0c;它集成了大量常用的框架和工具&#xff0c;大大简化了Spring项目的配置和部署。下面是Spring Boot的优缺点&#xff1a; 优点&#xff1a; 简化配置&#xff1a;Spring Boot自动配置功能可以根据应用的依赖自动配…...

Android Studio中创建apk签名文件

本文以macOS中Android Studio 2021.1.1版本为例介绍创建apk签名文件的操作步骤&#xff1a; 1.启动Android Studio&#xff0c;并打开一个Android项目。 2.依次点击菜单&#xff1a;Build -> Generate Signed Bundle / APK...。 3.在弹出的"Generate Signed Bundle or …...

CRC32 JAVA C#实现

项目中用到CRC32进行校验得地方&#xff0c;需要用到C#和java进行对比&#xff1a; 一、C#实现&#xff1a; class CRC32Cls { protected ulong[] Crc32Table; //生成CRC32码表 public void GetCRC32Table() { ulong Crc; …...

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——5Webscoket节点的使用

本科阶段最后一次竞赛Vlog——2024年智能车大赛智慧医疗组准备全过程——5Webscoket节点的使用 ​ 有了前面几篇文章的铺垫&#xff0c;现在已经可以实现我到手测试那一步的 1.解读usb_websocket_display.launch.py ​ 首先进入这个目录/root/dev_ws/src/origincar/originca…...

深入学习小程序第二天:事件处理与用户交互

一、概念 1. 事件绑定与类型 在小程序中&#xff0c;通过在组件上添加特定的属性&#xff08;如 bind 开头的属性&#xff09;来绑定事件处理函数&#xff0c;以响应用户的交互操作。常见的事件类型包括触摸事件、表单事件和系统事件&#xff1a; 触摸事件&#xff1a;用于响…...

操作系统快速入门(一)

&#x1f600;前言 本篇博文是关于操作系统的&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章可以帮助到大家&#xff0c;您的满意是我的动力&#x1f609;&…...

Spring Cloud微服务性能优化:策略、实践与未来趋势

标题&#xff1a;Spring Cloud微服务性能优化&#xff1a;策略、实践与未来趋势 摘要 在微服务架构中&#xff0c;服务调用链路的性能优化是确保系统高效运行的关键。Spring Cloud作为微服务架构的主流实现之一&#xff0c;提供了多种工具和方法来优化服务间的调用。本文将深…...

秒懂C++之多态

目录 一. 多态的概念 二. 多态的定义及实现 多态的构成条件 虚函数重写的例外 协变(基类与派生类虚函数返回值类型不同) 析构函数的重写(基类与派生类析构函数的名字不同) 练习例题 final override 重载、覆盖(重写)、隐藏(重定义)的对比 三. 抽象类 四. 多态的原理…...

C语言:求最大数不用数组

&#xff08;1&#xff09;题目&#xff1a; 输入一批正数用空格隔开&#xff0c;个数不限&#xff0c;输入0时结束循环&#xff0c;并且输出这批整数的最大值。 &#xff08;2&#xff09;代码&#xff1a; #include "stdio.h" int main() {int max 0; // 假设输入…...

零门槛成为HelpLook推荐官,邀好友加入,奖励享不停!

什么&#xff01;&#xff1f; 还有谁不知道HelpLook推荐官计划&#xff01; 只需要简单地注册加入 在好友成功订阅套餐之后 可一次性获得20%的丰厚现金返佣 HelpLook是一款快速搭建AI知识库的系统&#xff0c;并帮助企业0代码搭建帮助中心、FAQs、SOPs、产品文档、说明书和…...

基于python的图书馆大数据可视化分析系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;本人精通Java、Python、C#、C、C编程语言&#xff0c;同时也熟练掌握微信小程序、Php和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我有丰富的成品Java、Python、C#毕设项目经验&#xff0c;能够为学生提供各类…...

利用formdata自动序列化和xhr上传表单到后端

//FormData对象是XMLHTTPRequest level2新增的类型&#xff0c;它可以自动序列化表单内容&#xff0c;不再需要我们去写序列化表单方法&#xff1b; FormData()即可以直接把整个表单给它&#xff0c;也可以分别使用append(‘字段’,‘值’)方法给FormData()&#xff1b; 现在就…...

视频号小店大地震?还好我看了原文

关注卢松松&#xff0c;会经常给你分享一些我的经验和观点。 我X&#xff0c;如果不是看了原文&#xff0c;我差点也上当了。虽然视频号小店关闭了450个类目&#xff0c;但又重新开放了412个类目啊。 昨天&#xff08;8月9日&#xff09;&#xff0c;视频号一口气发了10个公…...

Genymotion adb shell

Genymotion 账户是 qq邮箱 参考 Ubuntu 20.04 安装 Android 模拟器 Genymotion https://www.zzzmh.cn/post/553cd96d4e47490a90b3302a76a93c0d Genymotion adb shell adb shell C:\Program Files\Genymobile\Genymotion\tools>adb shell lsusb Bus 001 Device 001: ID …...

探索AI与社交的交汇点:看Facebook如何引领智能化革命

在当今数字化时代&#xff0c;人工智能&#xff08;AI&#xff09;正成为各大科技公司变革的重要驱动力。作为全球领先的社交媒体平台&#xff0c;Facebook&#xff08;现Meta Platforms&#xff09;正处于这一智能化革命的前沿。通过不断创新和应用AI技术&#xff0c;Facebook…...

JVM 加载阶段 Class对象加载位置是在 堆中还是方法区?

在JVM&#xff08;Java虚拟机&#xff09;的类加载过程中&#xff0c;Class对象的加载位置涉及到堆&#xff08;Heap&#xff09;和方法区&#xff08;Method Area&#xff09;两个关键区域。具体来说&#xff0c;类的加载阶段涉及到将类的.class文件中的二进制数据读入到内存中…...

Android 获取短信验证

Android 获取短信验证 Android 获取短信验证 输入发短信的手机号&#xff0c;点击获取验证码&#xff0c;等接收到验证码后就会自动获取 SmsReceiver.Java import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; impor…...

负载均衡详细概念介绍之(四层和七层实现)

目录 一、负载均衡介绍 1.1什么是负载均衡 ​编辑 1.2 为什么要用负载均衡 二、负载均衡的类型 2.1 通过一些硬件实现 2.2 四层负载均衡 2.3 七层负载均衡 三、四层和七层的区别 及特点 一、负载均衡介绍 1.1什么是负载均衡 负载均衡:Load Balance&#xff0c;简称LB&a…...

GitHub Actions缓存终极升级指南:从v3到v5的平滑迁移路径

GitHub Actions缓存终极升级指南&#xff1a;从v3到v5的平滑迁移路径 【免费下载链接】cache Cache dependencies and build outputs in GitHub Actions 项目地址: https://gitcode.com/gh_mirrors/cach/cache GitHub Actions缓存是加速CI/CD工作流程的关键工具&#xf…...

免费PDM阅读器、PDM查看器、PDM文件阅读、PDM文件查看,轻松解析数据库结构

码猴PDMReader收费了&#xff0c;只能自己写个PDM阅读软件了&#xff0c;界面简洁&#xff0c;基本功能齐全&#xff0c;码猴PDMReader不支持的各类型数据库SQL语句生成、索引解析、SQL关键字高亮显示、批量导出SQL建表脚本也支持&#xff0c;共享出来&#xff0c;完全免费&…...

BepuPhysics2多线程架构解密:如何充分利用现代CPU实现并行物理仿真

BepuPhysics2多线程架构解密&#xff1a;如何充分利用现代CPU实现并行物理仿真 【免费下载链接】bepuphysics2 Pure C# 3D real time physics simulation library, now with a higher version number. 项目地址: https://gitcode.com/gh_mirrors/be/bepuphysics2 BepuPh…...

告别轮询!用STM32 HAL库的LIN主机模式,轻松实现汽车车窗控制(附完整代码)

告别轮询&#xff01;用STM32 HAL库的LIN主机模式&#xff0c;轻松实现汽车车窗控制 在汽车电子系统中&#xff0c;车窗控制看似简单&#xff0c;实则涉及复杂的通信协议和实时性要求。传统方案依赖硬连线或轮询机制&#xff0c;不仅布线复杂&#xff0c;还难以扩展。LIN总线作…...

华为云CCE内网部署Nacos集群实战:不用Helm,纯页面操作搞定镜像上传与配置

华为云CCE内网部署Nacos集群实战&#xff1a;纯控制台操作指南 在企业级容器化部署场景中&#xff0c;内网环境下的服务部署往往面临特殊挑战。当安全合规要求严格限制外网访问时&#xff0c;传统依赖公网镜像仓库和Helm工具的部署方案便不再适用。本文将详细介绍如何在华为云…...

SDMatte企业级应用:结合数据库实现大规模图片素材管理

SDMatte企业级应用&#xff1a;结合数据库实现大规模图片素材管理 1. 引言&#xff1a;企业图片管理的痛点与机遇 电商公司每天要处理上千张商品图片&#xff0c;设计师团队经常加班到深夜手动抠图。市场部门需要快速调用不同版本的素材&#xff0c;却总在混乱的文件夹里迷失…...

PyG实战:用自定义MessagePassing为异构图构建一个简单的推荐系统消息传递层

PyG实战&#xff1a;构建异构图的推荐系统消息传递层 当我们在电商平台上浏览商品时&#xff0c;系统总能精准推荐我们可能感兴趣的内容。这背后往往隐藏着一个复杂的用户-商品交互网络&#xff0c;而图神经网络(GNN)正是处理这类异构关系的利器。今天&#xff0c;我们就来探索…...

Qwen3-ForcedAligner-0.6B模型量化实战:减小部署体积

Qwen3-ForcedAligner-0.6B模型量化实战&#xff1a;减小部署体积 语音处理中的强制对齐技术&#xff0c;能够精确匹配文本与语音的时间戳&#xff0c;是语音识别、字幕生成等应用的关键环节。Qwen3-ForcedAligner-0.6B作为一款基于大语言模型的强制对齐工具&#xff0c;支持11种…...

基于DSP28335的三电平PCS系统代码功能说明

一、系统概述 本文档所分析的代码基于TI DSP28335处理器&#xff0c;实现了三电平储能变流器&#xff08;PCS&#xff09;的完整控制逻辑。该系统支持并网/离网双模式运行&#xff0c;具备多目标控制策略&#xff08;有功、无功、谐波治理、不平衡补偿等&#xff09;、完善的故…...

Phi-4-mini-reasoning轻量模型选型指南:何时该用Phi-4-mini而非Qwen3

Phi-4-mini-reasoning轻量模型选型指南&#xff1a;何时该用Phi-4-mini而非Qwen3 1. 模型概述与核心优势 Phi-4-mini-reasoning是一个基于合成数据构建的轻量级开源模型&#xff0c;专注于高质量、密集推理的数据处理能力。作为Phi-4模型家族成员&#xff0c;它特别适合需要高…...