Golang 协程/线程/进程 区别以及 GMP 详解

在 Golang 中,协程(goroutine)、线程(thread)和进程(process)是并发编程中的重要概念。让我们逐个介绍它们的区别和特点:

  1. 协程(goroutine):
  • 协程是 Go 语言中的轻量级线程,由 Go 运行时(runtime)管理。
  • 它是一种并发执行的单位,比线程更轻量级,启动和销毁开销较小。
  • Go 语言使用关键字 go 来启动一个协程,它会在后台运行,与其他协程并发执行。
  • 协程之间通过通道(channel)进行通信,实现数据的传递和同步。
  • 协程可以方便地进行并发编程,但是不能直接利用多核 CPU 的优势,因为默认情况下只会在一个操作系统线程上运行。
  1. 线程(thread):
  • 线程是操作系统调度的基本单位,用于执行程序中的指令。
  • 在操作系统级别,线程是由内核进行调度的。
  • 一个进程可以包含多个线程,它们共享进程的资源,如内存空间和文件描述符等。
  • 线程之间可以通过共享内存进行通信,但需要额外的同步机制来避免竞态条件和数据访问冲突。
  • 在多核 CPU 上,多个线程可以并行执行,利用了多核的优势。
  1. 进程(process):
  • 进程是操作系统中运行的一个程序的实例。
  • 进程是独立的、隔离的执行环境,拥有自己的内存空间和系统资源。
  • 进程之间通常通过进程间通信(IPC)机制进行通信,如管道、信号量、套接字等。
  • 进程之间的切换开销较大,但它们提供了更高的隔离性和安全性。

Golang 的运行时系统使用了 GMP(Goroutine Message Passing)模型来管理协程的调度和执行。下面是对 GMP 的一些详细解释:

GMP 是 Golang 运行时系统中的一种并发模型,用于实现协程的调度和管理。

GMP 模型将协程(goroutine)映射到操作系统线程(OS thread)上,使得多个协程可以在多个操作系统线程上并发执行。

在 GMP 模型中,有三种类型的实体:

  1. Goroutine(协程):代表可执行的代码块,是 Golang 并发的基本单位。
  2. M(操作系统线程):代表操作系统线程,负责执行协程。
  3. 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 的基本概念。

你可能感兴趣的:(golang)