资讯动态

进程、线程、协程有什么区别?从资源分配到调度,一篇理清

发布时间:2026/10/15 4:01:49 来源:尧图企业网站定制
一个进程里开两个线程去改同一个数组一个线程写进去的值另一个线程立刻就能读出来把同样的逻辑改成两个进程就读不到了得改用共享内存之类的东西才能互相看到。这个差别不是语言给的是操作系统在这两个东西身上分配的资源不一样。进程、线程、协程经常被摆在一起比但它们其实站在三个层次上进程是分配资源的单位线程是CPU 调度的单位协程是用户态里自己调度的单位。下面按这三个层次来拆。资源分配先看一样东西在 Linux 上跑ps -eLfUID PID PPID LWP C NLWP CMD wuwu 100 99 100 0 3 ./demo wuwu 100 99 101 0 3 ./demo wuwu 100 99 102 0 3 ./demo中间省略了STIME、TTY、TIME等列同一个程序三行PID都是 100LWP(轻量级进程就是线程 ID) 分别是 100、101、102。也就是说Linux 内核里面进程和线程并不是两种不同的东西区别只在于共享不共享资源。每个进程真正独占的是这几样资源进程内各线程虚拟地址空间(页表)共享同一份文件描述符表共享信号处理表共享当前工作目录、umask共享栈每个线程一份寄存器(PC、SP)每个线程一份线程本地存储 TLS每个线程一份共享什么并不是写死的。fork()和pthread_create()底层是同一个系统调用clone()只是传的 flag 不同flag效果CLONE_VM父子共享地址空间(页表)CLONE_FILES共享文件描述符表CLONE_FS共享 cwd、根目录、umaskCLONE_SIGHAND共享信号处理表CLONE_THREAD放进同一个线程组PID 相同fork()一个 flag 都不传所以是完整的拷贝(地址空间靠写时复制 COW先共享物理页写的时候才复制)pthread_create()会带上CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_THREAD这几个(另外还有几个管理用的)于是新东西和调用者共享地址空间和一大堆表只独立出栈和寄存器——这就是线程。所以线程是轻量级进程这个说法不是比喻。内核里它们就是同一套task_struct区别全在clone的那几个 flag 上。调度地址空间是分配给谁的问题调度是CPU 现在跑谁的问题。Linux 的调度器 CFS 调度的单位是task_struct它不区分进程和线程一律按线程调度。一个进程哪怕只有一个线程在调度器眼里也就是一个 task。/proc/pid/task/下面每个线程一个目录也能看出内核是按线程记账的。区别在这里一次线程切换CPU 换一下栈指针和一组寄存器就完了因为大家用的是同一张页表一次进程切换除了换寄存器还得换页表基址寄存器(CR3)这一步会牵连到 TLB。切换开销页表一换TLB(Translation Lookaside Buffer缓存虚拟页→物理页映射的那张快表)里的映射就全不对了因为它们是按上一个进程的虚拟地址存的。于是切换后的一段时间里访问内存几乎每次都 TLB miss要重新走一遍页表(可能得访存好几次)程序莫名其妙就慢了。这叫TLB 抖动。缓解手段是PCID(Process Context IDentifier)给 TLB 每一项打个进程标签切进程时不整个清空只是别人的项不再命中。但同一个物理页对应的 CPU 缓存(L1/L2)该污染的还是会污染。切换类型大致开销主要成本进程切换几微秒换 CR3 TLB 失效 保存恢复寄存器/栈线程切换亚微秒只换寄存器/栈不换页表协程切换几十纳秒用户态换少量寄存器不陷入内核这些数字是量级具体和 CPU、缓存命中情况都有关系。差距的来源就是上面两行要不要换页表要不要进内核。协程前面两种切换都要陷入内核(从用户态进内核态下一篇会专门讲)而这一步本身就贵。协程的思路是既然贵在内核掺和那干脆不进内核在用户态自己切。协程的调度器在用户态(Go 的 runtime、Python 的 asyncio 事件循环、Lua 的 coroutine)它自己维护一堆待跑的协程谁该跑了由它决定切换就是保存/恢复几个寄存器、换一下用户栈全程不碰内核。两个直接结果栈可以很小。内核线程的栈受ulimit -s限制Linux 默认 8MB 一个开几千个就没了Go 的 goroutine 初始栈只有几 KB还能按需增长所以一个进程里跑几十万个协程是常态。切换是协作式的。协程不会在任意时刻被抢走它得自己主动让出(执行到await、yield或者 Go 里运行时发现它要阻塞 IO 了)没有抢占也就没有随时可能到来的中断。代价是协程只是用户态的抽象干活的内核线程数没变。某一刻真正并行执行的协程上限就是底层内核线程数(Go 里由GOMAXPROCS控制)协程再多也是它们轮流跑。三者对比进程线程协程资源分配独立的地址空间、fd 表、信号表共享所属进程的这些共享所属线程的一切谁调度内核内核用户态 runtime切换开销微秒级换页表 TLB 失效亚微秒不换页表纳秒级不进内核栈独立地址空间独立栈默认 MB 级独立栈初始 KB 级数据交换必须走 IPC直接读写共享内存直接读写一个崩了只影响自己整个进程一起挂整个进程一起挂数量级几百到几千几千几十万所以实际怎么选需要强隔离的(浏览器给每个标签页一个进程)用进程需要大量共享数据、频繁通信的用线程并发数要上万、切换开销成了瓶颈的再考虑协程。但要注意协程不解决并行——它省的是切换和内存不是 CPU。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑