thumbnail
协程和线程的区别究竟是什么?
由 ChatGPT 生成的文章摘要
这篇文章探讨了协程和线程之间的区别,主要集中在调度方式及其实现上。作者通过与朋友的讨论,思考了抢占式调度与非抢占式调度的不同,并指出协程在调度时不一定都是非抢占式的。文章指出,协程的本质是一种回调机制,是组织代码的一种方式,而线程则是执行这些代码的载体。最终,尽管关于协程和线程的讨论对编程并无直接帮助,但文章希望能为读者提供一种理解协程的新视角。

概述

前段时间看到朋友 @贺兰星辰 发布了 Bilibili – 过去、现在和未来 —— Java 的现代化之路,其中在 5:38 附近开始提及协程:

file

这引起了我对线程协程区别的思考及其调度方式的思考。尽管屏幕上的四个词“用户态线程”“用户态调度”“轻量级”和“降低心智负担”都在提及协程被广为使用,但都感觉有那么一点隔靴搔痒。尤其是“用户态线程”一词,更令人疑惑。因此 2025 年 3 月 7 日中午与贺兰开展了一次讨论,先将相关内容和后续思考简记如下。

思考

抢占式调度 vs 非抢占式调度?

起初我认为协程线程的核心区别在于调度方式,其区别在于触发调度的代码是否是程序主动执行的。值得一提的是,程序主动执行的代码不一定是程序自身的代码,也可能是编译器或运行时会插入和执行的代码,总之,它是开发者可以预见将在运行时在当前进程内执行的代码。

协程有挂起点,只在此处插入代码以检查是否需要切换协程。由于无需在任意时刻调度,不需要中断之类的机制,所以可以在用户态实现非抢占式调度,由此也得出了其“用户态调度”和“轻量级”的特点;而线程采用抢占式调度,进而二者得以区分。

贺兰指出此理解错误,因为仍然存在抢占式调度的协程,而且“有栈协程一般都是抢占式的”,因此其并不能作为区分依据。

对于第一点,经过了解,Go 协程的信号量调度机制确实是抢占式调度的,它是为了避免协程进行 CPU 密集型计算时,连续运行太长时间而设置的。这种抢占式调度需要操作系统的参与,已经一定程度上与其“用户态调度”冲突,且并不普遍,应当视作协程实现上的差异,而非协程本身的特点。

对于第二点,有栈协程和是否抢占实现并没有什么关系,它也可以主动保存上下文(例如 Linux 的 getcontextsetcontext)并切换协程,也看不到倾向于使用抢占式调度的动机。

由此观之,协程的实现确实多样,它不一定是“用户态”的,不一定是非抢占式调度的。那协程线程的区别到底是什么呢?

代码形式 vs 执行载体?

协程的本质是回调:可以像写同步代码那样,在挂起点后写回调代码。为了支持通过协程组织代码,各种语言有自己的协程实现方法(或曰“协程实现”),它们并不影响“协程到底是什么”的答案。在前面的讨论中,我们正是因为没有分清协程协程实现的区别,误把协程实现的特点作为协程的特点,从而陷入了疑惑。

抛开有栈无栈和调度方式的迷雾,不难发现协程是组织程序代码的一种方法,而线程则是执行这些代码的载体

后记

实际上,关于协程线程区别的讨论并不能对我们编程带来什么帮助。不过,倘若能为读者提供一个看待协程的思路,那这次讨论和本文便发挥其最大作用了。

Not by AI

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇