系统线程和goroutine的区别

本文对比了Go语言中的goroutine与操作系统线程在栈空间、调度开销和标识方面的区别。goroutine具有动态调整栈空间的能力,调度开销小,且没有可供程序员使用的标识,旨在提供更高效、简洁的并发编程体验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从栈空间上,goroutine的栈空间更加动态灵活。

每个OS的线程都有一个固定大小的栈内存,通常是2MB,栈内存用于保存线程执行期间的局部变量,且大小是固定不变的,在多变的场景下,这样固定大小的栈,既太大,又太小,往往不能满足多变的场景。
2MB固定大小的栈,对于执行简单操作的goroutine来说,是一种巨大的浪费;但对于执行高度复杂的goroutine来说,又太过于小了。为了适应不同场景,goroutine在生命周期开始时只有一个很小的栈,典型情况是2KB, 在go程序中,一次创建十万左右的goroutine也不罕见(2KB*100,000=200MB)。而且goroutine的栈不是固定大小,它可以按需增大和缩小,最大限制可以到1GB。

从调度上看,goroutine的调度开销远远小于线程调度开销。

OS的线程由OS内核调度,每隔几毫秒,硬件就发送中断到CPU,CPU调用调度器内核函数,进而引发当前线程的暂停和下个线程的运行,而线程与线程之间的切换,需要一个完整的上下文切换。由于引发内存访问数量的增加和CPU等待周期的增加,这一操作是非常耗时的。
但是Go运行的时候包含一个自己的调度器,这个调度器使用一个称为一个M:N调度技术,把m个goroutine调度到n个os线程上运行,我们可以用GOMAXPROCS来控制n的数量,Go的调度器不是由硬件时钟来定期触发的,而是由特定的go语言结构来触发的,全部在用户态实现,不需要切换到内核语境,因此调度一个goroutine比调度一个线程的成本低很多。

goroutine没有标识

在大部分支持多线程的操作系统和编程语言中,线程有一个独特的标识,通常是一个整数或者指针,这个特性可以让我们构建一个线程的局部存储,本质是一个全局的map,以线程的标识作为键,这样每个线程可以独立使用这个map存储和获取值,不受其他线程干扰。
goroutine中没有可供程序员访问的标识,原因是一种纯函数的理念,不希望滥用线程局部存储导致一个不健康的超距作用,即函数的行为不仅取决于它的参数,还要取决于运行它的线程标识,这就造成了函数的行为变得诡异莫测,与Go语言所鼓励的简单的编程风格不匹配。

参考:

### Goroutine 操作系统线程区别 #### 定义与创建 GoroutineGo 语言中用于实现并发的基本执行单元,由 Go 运行时管理。它比传统的操作系统线程更加轻量级,可以轻松地创建成千上万个 Goroutine 而不会消耗大量的系统资源。相比之下,操作系统线程是由操作系统内核直接管理调度的执行单元,每个线程都需要独立的栈空间上下文信息,这使得它们在创建、销毁以及切换时开销较大 [^4]。 ```go // 创建一个简单的 Goroutine go func() { fmt.Println("Hello from a goroutine") }() ``` #### 资源占用 Goroutine 的初始栈大小非常小,大约为 2KB,并且可以根据需要动态增长或缩小。这种设计减少了内存的使用并提高了效率。而操作系统线程通常拥有固定的栈大小(一般为几 MB),即使实际使用的内存远小于这个数值,也会预留出相应的空间,导致大量线程存在时会占用大量的内存资源 [^4]。 #### 上下文切换成本 线程之间的上下文切换涉及到用户态到内核态的转换,这是一个相对昂贵的操作;而 Goroutine 间的切换仅发生在用户态,因此其上下文切换的成本要低得多。这是因为 Go 运行时负责了 Goroutine调度工作,而不是依赖于操作系统调度器 [^4]。 #### 调度方式 操作系统线程采用的是抢占式调度机制,即操作系统决定何时暂停一个线程以运行另一个线程。这种方式虽然能够保证公平性,但同时也引入了较高的上下文切换开销。相反,Goroutine 使用协作式的调度策略,这意味着只有当当前正在执行的 Goroutine 主动放弃 CPU(例如通过 channel 的发送/接收操作)或者被阻塞时才会进行调度切换。这样的做法降低了切换成本但也要求程序员编写非阻塞代码来充分利用并发优势 [^4]。 #### 通信与同步 对于线程来说,共享内存是主要的通信手段,这就需要额外的锁机制如互斥量(mutex)等来确保数据一致性。而在 Go 中推荐使用 channel 来作为不同 Goroutine 之间传递数据的主要途径,这样不仅简化了编程模型还隐含着对共享内存访问的一种规避,从而减少了死锁等问题的发生概率 [^3]。 #### 应用场景 由于上述特性,Goroutine 更适合处理高并发网络服务这类任务,在这些场景下可能同时存在数万个活跃的执行单元。而对于那些对响应时间有严格要求的应用程序或者是需要更细粒度控制底层硬件行为的情况,则可能会倾向于选择传统线程 [^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值