开启掘金成长之旅!这是我参与「掘金日新计划 · 6 月更文挑战」的第 14 天,点击查看活动详情
在 Golang 中,协程(goroutine)、线程(thread)和进程(process)是并发编程中的重要概念。让我们逐个介绍它们的区别和特点:
- 协程(goroutine):
- 协程是 Go 语言中的轻量级线程,由 Go 运行时(runtime)管理。
- 它是一种并发执行的单位,比线程更轻量级,启动和销毁开销较小。
- Go 语言使用关键字
go来启动一个协程,它会在后台运行,与其他协程并发执行。 - 协程之间通过通道(channel)进行通信,实现数据的传递和同步。
- 协程可以方便地进行并发编程,但是不能直接利用多核 CPU 的优势,因为默认情况下只会在一个操作系统线程上运行。
- 线程(thread):
- 线程是操作系统调度的基本单位,用于执行程序中的指令。
- 在操作系统级别,线程是由内核进行调度的。
- 一个进程可以包含多个线程,它们共享进程的资源,如内存空间和文件描述符等。
- 线程之间可以通过共享内存进行通信,但需要额外的同步机制来避免竞态条件和数据访问冲突。
- 在多核 CPU 上,多个线程可以并行执行,利用了多核的优势。
- 进程(process):
- 进程是操作系统中运行的一个程序的实例。
- 进程是独立的、隔离的执行环境,拥有自己的内存空间和系统资源。
- 进程之间通常通过进程间通信(IPC)机制进行通信,如管道、信号量、套接字等。
- 进程之间的切换开销较大,但它们提供了更高的隔离性和安全性。
Golang 的运行时系统使用了 GMP(Goroutine Message Passing)模型来管理协程的调度和执行。下面是对 GMP 的一些详细解释:
GMP 是 Golang 运行时系统中的一种并发模型,用于实现协程的调度和管理。
GMP 模型将协程(goroutine)映射到操作系统线程(OS thread)上,使得多个协程可以在多个操作系统线程上并发执行。
在 GMP 模型中,有三种类型的实体:
- Goroutine(协程):代表可执行的代码块,是 Golang 并发的基本单位。
- M(操作系统线程):代表操作系统线程,负责执行协程。
- P(处理器上下文):代表处理器上下文,负责调度协程到操作系统线程上执行。
在 GMP 模型中,存在一个全局运行队列(global runqueue)和每个 M 对应的本地运行队列(local runqueue)。
- 当一个 M 空闲时,它会从全局运行队列中获取一个协程,并将其放入本地运行队列,然后执行该协程。
- 如果本地运行队列为空,M 会去全局运行队列获取协程。
- 当一个协程被阻塞(如等待 I/O 操作完成)时,M 会释放操作系统线程,允许其他协程执行。
- 当一个协程被唤醒时,M 可能会重新获取操作系统线程,并执行该协程。
具体代码实例:
package main
import (
"fmt"
"runtime"
"sync"
)
func main() {
// 协程示例
go hello()
// 线程示例
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("This is a thread")
}()
// 进程示例
fmt.Println("This is a process")
// 等待协程和线程执行完毕
wg.Wait()
}
func hello() {
fmt.Println("Hello, goroutine!")
}
func init() {
// 设置 GOMAXPROCS(操作系统线程数)为 1,以模拟单线程模式
runtime.GOMAXPROCS(1)
}
我们使用了 go 关键字来启动一个协程,执行了一个简单的 hello 函数。同时,我们使用了 sync.WaitGroup 来等待线程的执行完成。在 init 函数中,我们将 GOMAXPROCS 设置为 1,以模拟单线程模式。
请注意:具体的并发模型和调度行为取决于 Golang 运行时系统的实现,可能会因不同版本和配置而有所不同。以上代码只是一个简单示例,用于演示协程、线程和进程的区别,并介绍了 GMP 的基本概念。