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

有关Groutine无限创建的分析

有关Groutine无限创建的分析

文章目录

  • 有关Groutine无限创建的分析
    • 从操作系统分析进程、线程、协程的区别
      • 进程内存
      • 线程内存
      • 执行单元
    • cpu切换成本
      • 协程切换成本
      • 线程切换成本
      • 内存占用
    • Go程是否可以无限创建
      • 不控制go程创建引发的问题
      • 简单方式控制go程创建
        • channel有buffer
        • sync.WaitGroup
        • channel有buffer + sync.WaitGroup
        • channel无buffer + (初始准备一定数量的协程)任务发送 和 任务执行 分离
    • 总结

从操作系统分析进程、线程、协程的区别

在操作系统中,进程是就是一个程序运行。他是独占一个内存空间的个体。操作系统分配资源时,就是按照进程的极限内存大小分配的。因此,进程是操作系统资源分配的最小单元。线程是进程的一部分,多个线程构成一个进程。在一个进程中,有一块全局的内存空间(堆空间)堆空间是对所有线程共享的,每个线程都有一个独立的栈空间。因此,线程可以独立运行。cpu分配时间片就是按照进程中线程的数量分的。所以,线程是cpu调度的最小单元。由于线程的开辟受限于硬件资源,为了适应高并发的场景,增加并发量,减少cpu上下文的切换开销。我们提出了协程,协程是工作于用户态的。每个协程需要绑定一个线程才可以正常运行。协程的大小是线程的1/400000。因此可以极大的减少内存开销,同时协程上下文的切换是在用户态的,不需要用户态->内核态的转变,极大的减少了cpu的时间资源。

进程内存

首先,进程内存是一个独立的内存块,向os申请的一块虚拟内存。进程的内存由几部分组成:

内存区域作用特点
代码段(Text)存储进程的可执行代码(机器指令)只读,多个进程可共享同一份代码(如共享库)
数据段(Data)存储已初始化的全局变量和静态变量(如 static int a = 10;读写权限,程序运行期间持续存在
BSS 段存储未初始化的全局变量和静态变量(初始化为 0 或 NULL)不占用可执行文件磁盘空间,运行时由操作系统自动清零
堆(Heap)动态内存分配区域(如 C 语言的 malloc、Go 的 new由程序主动申请 / 释放,大小不固定,可能产生内存碎片
栈(Stack)存储函数调用的局部变量、参数、返回地址等由操作系统自动管理,后进先出(LIFO),大小通常有限制(如 Linux 默认 8MB)
共享库 / 动态链接存储程序运行时加载的动态链接库(如 Linux 的 .so 文件)多个进程可共享同一库的内存实例,节省内存资源
内核空间进程访问内核服务时使用的内存区域(如系统调用、文件描述符表)每个进程共享内核空间,用户态程序不能直接访问

直接由操作系统调度,操作系统以进程为单位分配资源,因此进程是操作系统资源分配的最小单元。

线程内存

线程在进程的栈区有自己的栈区,其余部分,进程内的所有线程共享。共享使得线程之间的通信更容易,内存关联性很大,一个出现问题,导致其他线程出问题,进而影响进程。所以线程是cpu调度的最小单元。

执行单元

对于Linux来讲,对于进程和线程是相同的,都被视作一个单独的执行单位。所以,进程中的线程数量影响cpu时间片的分配。通过提高进程中线程的数量来提高进程在cpu中分配到的时间片的比例。那是不是开辟越多线程越好?当然不是:cpu切换一个执行单元会花费时间和性能开销。(原因:页表查询缓慢,切换使得cache失效使得缓存命中率低,宏观表现运行速度慢)

cpu切换成本

由于cpu切换需要花费大量成本,所以不能无限制的开辟线程,我们可以考虑在用户态进行切换执行流程,让cpu内核态不再执行切换流程。因此,我们就在用户态创建一个伪执行单元——协程。

协程切换成本

用户态:当前cpu寄存器状态保存,将需要切换进来的协程cpu寄存器状态加载到cpu寄存器上。

线程切换成本

  1. 用户态和内核态的切换
  2. 内核态:操作系统调度
  3. 上下文大小:除了和协程一样的上下文,还有线程特有的栈、寄存器等,上下文比协程的大一点

内存占用

可以使用命令查看内存:

ulimit -s

Go程是否可以无限创建

go程虽然每个也就2.5KB左右,但是也不能无限创建。从我们做实验就可以得到。最终会耗尽cpu资源,导致panic

不控制go程创建引发的问题

for i:=0;i<math.Maxint64;i++{go func(i int){do ...fmt.Println("go func ",i,"go程 count = ",runtime.NumGoroutine())}(i)
}

短时间占据操作系统资源:cpu、内存、文件描述符等。使得:cpu使用率浮动上涨、内存占用上涨、主进程崩溃(被杀掉)。

简单方式控制go程创建

channel有buffer
eg: make(chan bool,3)

可以,但是需要最后阻塞,保证所有任务都结束

sync.WaitGroup
eg: 
var wg = sync.WaitGroup{}
wg.Add(1)
wg.Done()
wg.Wait()

不可以,执行的速度远小于创建的速度。因为创建和执行都没有限制速度,只是保证开启的协程全部可以完成

channel有buffer + sync.WaitGroup

可以,在2的基础上可以控制生成速度。平衡 生成和消费的速度

channel无buffer + (初始准备一定数量的协程)任务发送 和 任务执行 分离

无buffer,那么生成的速度取决于消费的速度,当有协程接收任务时才有任务被生产到chan中。消费的速度取决于我们一开始生产的协程数目

总结

主要从操作系统的层面介绍了进程、线程、协程,并以cpu切换成本的角度对比了线程和协程的区别。后面,又介绍了go是否可以无限创建的问题。并提出如何有效控制go的数量。

http://www.xdnf.cn/news/539587.html

相关文章:

  • YOLO模型使用jupyterlab的方式进行预测/推理(示例)
  • Linux配置SSH密钥认证
  • 程序化 SEO 全攻略:如何高效提升网站排名?
  • 【python】返回所有匹配项的第一个元素、第二个元素。。。
  • 龙芯中科2024年度业绩说明会:企稳向好,布局未来!
  • 贵州某建筑物挡墙自动化监测
  • Dolphinscheduler执行工作流失败,后台报duplicate key错误
  • 如何通过生成式人工智能认证(GAI认证)提升自己的技能水平?
  • C++经典库介绍
  • PH热榜 | 2025-05-18
  • 微服务项目->在线oj系统(Java版 - 5)
  • 《Building effective agents》学习总结
  • C++中聚合类(Aggregate Class)知识详解和注意事项
  • 深入理解动态规划:从斐波那契数列到最优子结构
  • YoloV9改进策略:卷积篇|风车卷积|即插即用
  • 【Python-Day 15】深入探索 Python 字典 (下):常用方法、遍历、推导式与嵌套实战
  • C++容器适配器
  • DAPO:用于指令微调的直接偏好优化解读
  • 【idea 报错:java: 非法字符: ‘\ufeff‘】
  • 第二十一次博客打卡
  • 【C语言内存函数】--memcpy和memmove的使用和模拟实现,memset函数的使用,memcmp函数的使用
  • 1 asyncio模块
  • Ubuntu——配置静态IP
  • 基于Transformers与深度学习的微博评论情感分析及AI自动回复系统
  • 【C++】模版(1)
  • 基于不完美维修的定期检测与备件策略联合优化算法matlab仿真
  • megatron——EP并行
  • 商标名称起好后,尽快申请注册确权!
  • 【cursor疑惑】cursor续杯后使用agent对话时,提示“需要pro或商业订阅的用户才能使用“
  • 电路研究9.3.6——合宙Air780EP中的AT开发指南:FTP 应用指南