• 字节一面:go的协程相比线程,轻量在哪?


    1. 用户态和内核态

    Linux整个体系分为用户态和内核态(或者叫用户空间和内核空间), 那内核态究竟是什么呢?

    本质上我们所说的内核态, 它是一种特殊的软件程序,特殊在哪?
    统筹计算机的硬件资源,例如协调CPU资源、分配内存资源、并且提供稳定的环境供应用程序运行。

    2. 为什么线程切换会导致用户态和内核态的切换?

    • 线程是cpu调度的基本单位,进程是资源占有的基本单位。
    • 因为线程中的代码是在用户态运行,而线程的调度是在内核态,所以线程切换会触发用户态和内核态的切换。
    • 线程上下文切换的代价是高昂的:上下文切换的延迟取决于不同的因素,大概是50到100 ns左右,考虑到硬件平均在每个核心上每ns执行12条指令,那么一次上下文切换可能会花费600到1200条指令的延迟时间。

    3. 导致线程上下文切换的时机

    <1>. 自发性上下文切换 自发性上下文切换指线程由于自身因素导致的切出
    Thread.sleep() 线程主动休眠
    object.wait() 线程等待锁
    Thread.yield() 当前线程主动让出CPU,如果有其他就绪线程就执行其他线程,如果没有则继续当前线程
    oThread.join() 阻塞发起调用的线程,直到oThread执行完毕

    <2>. 非自发性上下文切换:线程由于线程调度器的原因被迫切出
    线程的时间片用完
    高优先级线程抢占
    虚拟机的垃圾回收动作

    4. 线程切换的开销

    <1>. 直接开销
    保存/恢复上下文所需的开销
    线程调度器调度线程的开销
    <2>. 间接开销
    重新加载高速缓存
    上下文切换可能导致 一级缓存被冲刷,写入下一级缓存或内存

    5. go的协程轻量级体现在哪?

    如上面所述,线程切换会导致 用户态和内核态的切换,其中内核态耗时较长,且不受用户代码控制。

    go将goroutine的调度维持在用户态, 这是由GPM中的P Process来完成的,做用户态任务的调度器,功能类比于常规的操作系统线程调度器,所以又被称为逻辑处理器。

    (1) 上下文切换代价小: P 是G、M之间的桥梁,调度器对于goroutine的调度,很明显也会有切换,这个切换是很轻量的: 只涉及PC SP DX三个寄存器的值的修改;
    而对比线程的上下文切换则需要陷入内核模式、以及16个寄存器的刷新。

    (2) 内存占用小: 线程栈空间通常是2M, Goroutine栈空间最小是2k, golang可以轻松支持10w+的goroutine运行,而线程数量到达1k, 内存占用就到2G。

    6. GO GMP 调度方式

    • 由逻辑处理器P调度协程G进系统线程M (若本地队列没有G,从其他队列/全局队列偷取G),
    • 线程M执行G, 遇到[系统调用], G和M分离,拿新的M去接管原逻辑处理器P

    请仔细阅读上图,出处不可考证,感谢原图作者。

    这里特意指出网络IO操作不会走上图的模型,否则要分配的系统线程M依然很多,程序很快就爆满了。这时G会和逻辑处理器P分离,并移动到netpoller,一旦网络轮询器通知网络读/写就绪,对应G就会重新分配到逻辑器处理器上来完成操作, 在此期间原系统线程M可以去做别的G。

    ref

  • 相关阅读:
    【Android笔记03】Android基本的UI控件(TextView、Button、EditText、ImageView、ProgressBar)
    glib的GHashTable实践
    基于SSM的健身房管理系统
    09-锚点&精灵图
    20231120进程优先级和调度策略
    使用 AI 编程助手 CodeWhisperer,开发如有神助
    示例:WPF中TreeView自定义TreeNode泛型绑定对象来实现级联勾选
    Linux性能基础:CPU、内存、磁盘等概述
    网工内推 | 急招网工,思科、华为认证优先,法定节假日三薪
    kmalloc、kzalloc、vmalloc、kmem_cache_alloc的区别
  • 原文地址:https://www.cnblogs.com/JulianHuang/p/16008107.html