GoLang协程Goroutiney原理与GMP模型详解

简介: 【11月更文挑战第4天】Goroutine 是 Go 语言中的轻量级线程,由 Go 运行时管理,创建和销毁开销小,适合高并发场景。其调度采用非抢占式和协作式多任务处理结合的方式。GMP 模型包括 G(Goroutine)、M(系统线程)和 P(逻辑处理器),通过工作窃取算法实现负载均衡,确保高效利用系统资源。
  1. Goroutine 原理
  • 轻量级线程概念
  • Goroutine 是 Go 语言中并发执行的基本单位,它类似于线程,但比传统的线程更加轻量。在操作系统层面,线程的创建、销毁和切换都有一定的开销。而 Goroutine 是由 Go 运行时(runtime)管理的用户态线程,它的创建和销毁的开销非常小,使得在 Go 程序中可以轻松地创建成千上万个 Goroutine 来实现高并发。
  • 例如,在一个处理网络请求的服务端程序中,可以为每个到来的请求创建一个 Goroutine 来处理,这样可以高效地处理大量并发请求。
  • 调度原理
  • Goroutine 的调度是由 Go 运行时的调度器完成的。当一个 Goroutine 被创建后,它会被放入一个调度队列中等待执行。调度器会根据一定的策略从队列中选取 Goroutine 并将其分配到系统线程(OS Thread)上执行。
  • 与操作系统的线程调度不同,Go 调度器采用了非抢占式多任务处理和协作式多任务处理相结合的方式。在非抢占式调度中,Goroutine 会一直执行,直到它主动让出 CPU 资源,比如通过调用runtime.Gosched()函数。而在某些特殊情况下,如 Goroutine 执行时间过长或者系统调用阻塞时,调度器也会进行抢占式调度,将 CPU 资源分配给其他等待的 Goroutine。
  1. GMP 模型详解
  • G(Goroutine)
  • G 代表 Goroutine,是 Go 程序中实际执行的代码片段。每个 Goroutine 都有自己的栈空间,初始栈大小一般比较小(通常为 2KB),并且可以根据需要动态增长。这与传统线程的固定栈大小相比,更有效地利用了内存资源。
  • 例如,一个简单的 Goroutine 可能是一个函数的执行体,像func() { println("Hello, Goroutine") }这样的函数可以作为一个 Goroutine 来运行。
  • M(Machine,系统线程)
  • M 代表系统线程,是操作系统层面的线程。Go 程序会利用操作系统的线程来执行 Goroutine。M 与内核线程直接相关,它负责执行 Goroutine 中的代码。Go 运行时会根据系统资源和 Goroutine 的数量来动态地创建和销毁 M。
  • 例如,在一个多核 CPU 的系统中,Go 程序可能会创建多个 M 来充分利用多核的优势,每个 M 可以执行一个或多个 Goroutine。
  • P(Processor,逻辑处理器)
  • P 是一个抽象的概念,代表逻辑处理器。它是连接 G 和 M 的桥梁,每个 P 都有一个本地队列(Local Queue),用于存放等待执行的 Goroutine。P 的数量在程序启动时可以通过GOMAXPROCS环境变量或者runtime.GOMAXPROCS()函数来设置,它通常与系统的 CPU 核心数相关,决定了 Go 程序可以同时并发执行的 Goroutine 的数量上限。
  • 例如,假设GOMAXPROCS设置为 4,那么 Go 程序最多可以有 4 个 P,也就意味着最多可以同时有 4 个 Goroutine 在不同的 CPU 核心上执行(不考虑超线程等情况)。
  • GMP 之间的协作关系
  • 当一个 Goroutine 被创建时,它会首先被放入一个 P 的本地队列中。M 会从 P 的本地队列中获取 Goroutine 来执行。如果 P 的本地队列中没有 Goroutine 了,M 会尝试从其他 P 的队列中 “窃取” Goroutine 来执行(工作窃取算法),这样可以保证各个 P 之间的负载均衡。
  • 当 M 因为系统调用等原因阻塞时,它会释放绑定的 P,使得 P 可以与其他 M 绑定继续执行 Goroutine。而当 M 完成系统调用返回后,它会尝试获取一个空闲的 P 或者从其他 P 那里 “窃取” Goroutine 来继续执行。这种机制保证了 Go 程序在面对各种复杂的系统调用和阻塞情况时,仍然能够高效地利用系统资源,保持 Goroutine 的高效调度。
相关文章
|
5天前
|
存储 算法 Java
GoLang GPM模型
本文介绍了 Go 语言中的 goroutine 及其调度器(Go Scheduler)的工作原理。goroutine 并非传统意义上的协程,而是基于两级线程模型实现的轻量级并发单元。文章详细解释了三种主流线程模型(内核级、用户级和两级线程模型)的特点,并重点阐述了 G-P-M 模型(Goroutine、Processor、Machine)的工作机制,包括调度算法、阻塞处理等。通过动态栈管理和高效的调度器,Go 程序能够轻松支持成千上万个并发任务。
GoLang GPM模型
|
4月前
|
存储 安全 测试技术
GoLang协程Goroutiney原理与GMP模型详解
本文详细介绍了Go语言中的Goroutine及其背后的GMP模型。Goroutine是Go语言中的一种轻量级线程,由Go运行时管理,支持高效的并发编程。文章讲解了Goroutine的创建、调度、上下文切换和栈管理等核心机制,并通过示例代码展示了如何使用Goroutine。GMP模型(Goroutine、Processor、Machine)是Go运行时调度Goroutine的基础,通过合理的调度策略,实现了高并发和高性能的程序执行。
316 29
|
4月前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
123 1
|
4月前
|
Go 计算机视觉
在Golang高并发环境中如何进行协程同步?
在此示例中,使用互斥锁来保护对共享计数器变量 c 的访问,确保并发的 HTTP 请求不会产生数据竞争。
62 3
|
6月前
|
Go
Golang语言之管道channel快速入门篇
这篇文章是关于Go语言中管道(channel)的快速入门教程,涵盖了管道的基本使用、有缓冲和无缓冲管道的区别、管道的关闭、遍历、协程和管道的协同工作、单向通道的使用以及select多路复用的详细案例和解释。
193 4
Golang语言之管道channel快速入门篇
|
6月前
|
Go
Golang语言文件操作快速入门篇
这篇文章是关于Go语言文件操作快速入门的教程,涵盖了文件的读取、写入、复制操作以及使用标准库中的ioutil、bufio、os等包进行文件操作的详细案例。
94 4
Golang语言文件操作快速入门篇
|
6月前
|
Go
Golang语言之gRPC程序设计示例
这篇文章是关于Golang语言使用gRPC进行程序设计的详细教程,涵盖了RPC协议的介绍、gRPC环境的搭建、Protocol Buffers的使用、gRPC服务的编写和通信示例。
161 3
Golang语言之gRPC程序设计示例
|
6月前
|
安全 Go
Golang语言goroutine协程并发安全及锁机制
这篇文章是关于Go语言中多协程操作同一数据问题、互斥锁Mutex和读写互斥锁RWMutex的详细介绍及使用案例,涵盖了如何使用这些同步原语来解决并发访问共享资源时的数据安全问题。
122 4
|
6月前
|
Go
Golang语言错误处理机制
这篇文章是关于Golang语言错误处理机制的教程,介绍了使用defer结合recover捕获错误、基于errors.New自定义错误以及使用panic抛出自定义错误的方法。
75 3
|
6月前
|
Go 调度
Golang语言goroutine协程篇
这篇文章是关于Go语言goroutine协程的详细教程,涵盖了并发编程的常见术语、goroutine的创建和调度、使用sync.WaitGroup控制协程退出以及如何通过GOMAXPROCS设置程序并发时占用的CPU逻辑核心数。
179 4
Golang语言goroutine协程篇