Go 协程 | 青训营

39 阅读3分钟

协程

协程(Goroutine)是一种轻量级的执行单元,可以在 Go 语言中创建数千甚至数百万个协程而不会产生显著的负担。与传统的线程相比,协程的创建和销毁成本非常低,因此可以在应用程序中更自由地使用。Go 语言中协程之间通过通道(Channel)进行通信,通道是一种用于协程间传递数据的机制。它可以确保数据的安全传递和同步

GOMAXPROCS

GOMAXPROCS 是 Go 语言中的一个环境变量,用于设置并发执行的最大 CPU 数量。Go 语言具有强大的并发支持,可以在多个 CPU 核心上同时运行 goroutines

package main
​
import (
    "fmt"
    "runtime"
)
​
func main() {
    numCores := runtime.NumCPU() // 获取可用的 CPU 核心数
    fmt.Println("Available CPU cores:", numCores)
​
    // 设置 GOMAXPROCS 为 4
    runtime.GOMAXPROCS(4)
​
    // 此后程序会使用多个 CPU 核心来运行 goroutines
}

什么是协程泄漏?

协程泄漏(Goroutine Leak)是指在并发编程中,协程(Goroutine)被意外地创建但没有被正确地关闭或释放,从而导致这些协程一直保持在运行状态,占用系统资源,最终可能导致应用程序性能下降或资源耗尽的问题。协程泄漏是一种常见的并发编程错误,可能会导致应用程序的稳定性和可靠性受到影响

协程泄漏可能发生在以下情况下:

  1. 协程未关闭。在创建协程后,如果没有正确地关闭协程,它将一直保持在运行状态,无法被垃圾回收。这可能是因为开发者忘记了关闭协程,或者在使用通道进行通信时没有正确处理退出条件
  2. 协程阻塞。如果协程在执行过程中发生阻塞(如等待通道数据、锁等),但是没有释放阻塞,那么该协程将一直占用资源而无法继续执行,从而导致资源浪费
  3. 循环创建协程。如果在循环中创建协程但没有适当地等待它们完成,可能会导致大量的协程被创建并保持运行,最终引发协程泄漏
  4. 递归调用。如果协程在递归函数中被创建,但没有正确地控制递归的深度或没有在适当的时候终止递归,可能会导致大量协程被创建而无法正常关闭

GMP 模型

GMP(Goroutine,Machine,Processor)调度模型是 Go 语言实现并发的关键部分,它为 Go 协程的创建、执行和资源管理提供了支持

  1. G (Goroutine) 。Goroutine 是 Go 语言中的轻量级执行单位。它是由 Go 运行时系统管理的,可以被看作是一个函数的执行实例。Goroutine 通过在多个操作系统线程之间调度来实现并发执行。由于其轻量级的特性,可以创建成千上万的 goroutine,而不会显著影响性能
  2. M (Machine) 。M 代表操作系统线程(machine thread),也称为 "工作线程"。Go 运行时系统通过 M 来执行 goroutine。M 的数量默认是系统的 CPU 核心数,但可以通过设置 GOMAXPROCS 环境变量来调整。M 负责运行 goroutine,并负责线程之间的调度和同步
  3. P (Processor) 。P 代表处理器(processor)。它是执行 goroutine 的上下文,其中包含了运行 goroutine 所需的栈和寄存器状态。P 负责将 M 与 G 关联起来,并将 goroutine 映射到 M 上,从而实现并行执行