Golang并发机制及CSP并发模型

Golang 并发机制及 CSP 并发模型

Golang 是一门为并发而生的语言,其并发机制基于 CSP(Communicating Sequential Processes,通信顺序过程) 模型。CSP 是一种描述并发系统中交互模式的正式语言,强调通过通信来共享内存,而不是通过共享内存来通信。


CSP 模型简介

CSP 模型由 Tony Hoare 在 20 世纪 70 年代提出,是一种基于消息传递的并发模型。其核心思想是:

  • 通信代替共享:并发实体(如 Goroutine)通过 Channel 进行通信,而不是直接共享内存。
  • Channel 是第一类对象:Channel 是 CSP 模型的核心,用于在并发实体之间传递消息。
  • 同步通信:发送和接收操作是阻塞的,确保消息的同步传递。

CSP 模型的优势在于:

  • 避免了传统多线程编程中的锁竞争问题。
  • 简化了并发编程的复杂性。
  • 提高了程序的可读性和可维护性。

Golang 中的 CSP 实现

Golang 通过 GoroutineChannel 实现了 CSP 模型。

1. Goroutine

Goroutine 是 Golang 中的并发执行单位,类似于线程,但比线程更轻量。其特点包括:

  • 用户态调度:Goroutine 由 Go 运行时调度,避免了内核态和用户态的切换开销。
  • 低成本创建:每个 Goroutine 的栈空间初始很小(通常为 2KB),可以轻松创建成千上万的 Goroutine。
  • 动态扩展:Goroutine 的栈空间会根据需要动态扩展和收缩。
2. Channel

Channel 是 Goroutine 之间的通信机制,用于传递数据。其特点包括:

  • 类型安全:Channel 有明确的类型,只能传递指定类型的数据。
  • 同步通信:发送和接收操作是阻塞的,确保数据的同步传递。
  • 双向或单向:Channel 可以是双向的,也可以是单向的(只发送或只接收)。

GPM 调度模型

Golang 的并发调度由 GPM 模型 实现,即:

  • G(Goroutine):用户级的轻量级线程,每个 Goroutine 保存自己的上下文信息。
  • P(Processor):逻辑处理器,负责调度 Goroutine 和线程(M)之间的关联关系。
  • M(Machine):操作系统线程,负责执行 Goroutine。
GPM 的工作原理
  1. 初始化:Go 程序启动时,会创建多个 P(默认等于 CPU 核心数),每个 P 绑定一个 M。
  2. Goroutine 调度
    • 每个 P 维护一个本地 Goroutine 队列(Local Runqueue)。
    • 当一个 Goroutine 被创建时,会被放入某个 P 的本地队列中。
    • M 从绑定的 P 的本地队列中获取 Goroutine 并执行。
  3. 阻塞处理
    • 如果 Goroutine 发生阻塞(如 IO 操作),M 会释放 P,P 会绑定到另一个 M 继续执行其他 Goroutine。
    • 当阻塞的 Goroutine 恢复时,会被放入全局队列(Global Runqueue)或某个 P 的本地队列中。
  4. 负载均衡
    • 如果某个 P 的本地队列为空,它会从全局队列或其他 P 的本地队列中"偷取" Goroutine。
    • 如果全局队列也为空,M 会进入休眠状态,直到有新的 Goroutine 被创建。

是 否 GPM 调度模型 G Goroutine P Processor M Machine 用户级轻量级线程 保存上下文信息 逻辑处理器 调度 Goroutine 和 M 的关系 操作系统线程 执行 Goroutine 维护本地 Goroutine 队列 从队列中获取 Goroutine 执行 Goroutine 是否阻塞? 释放 PM 绑定到其他 P 继续执行 阻塞恢复后放入全局队列 其他 P 从全局队列中偷取 Goroutine

以下是 Golang 中使用 Goroutine 和 Channel 实现并发通信的示例:
cpp 复制代码
package main

import (
    "fmt"
    "time"
)

func worker(id int, jobs <-chan int, results chan<- int) {
    for job := range jobs {
        fmt.Printf("Worker %d started job %d\n", id, job)
        time.Sleep(time.Second) // 模拟任务执行
        fmt.Printf("Worker %d finished job %d\n", id, job)
        results <- job * 2 // 将结果发送到 results Channel
    }
}

func main() {
    const numJobs = 5
    jobs := make(chan int, numJobs)
    results := make(chan int, numJobs)

    // 启动 3 个 Worker Goroutine
    for w := 1; w <= 3; w++ {
        go worker(w, jobs, results)
    }

    // 发送任务到 jobs Channel
    for j := 1; j <= numJobs; j++ {
        jobs <- j
    }
    close(jobs)

    // 从 results Channel 中接收结果
    for a := 1; a <= numJobs; a++ {
        fmt.Println("Result:", <-results)
    }
}
相关推荐
用心尝试2 分钟前
23种设计模式的cpp举例
c++·设计模式
我命由我123458 分钟前
TCP 协议相关的参数(KeepAliveTime、KeepAliveInterval、TcpMaxDataRetransmissions)
java·网络·后端·网络协议·tcp/ip·java-ee·信息与通信
落落落sss13 分钟前
spring-data-mongoDB
java·服务器·数据库·后端·python·mongodb·spring
web1350858863514 分钟前
10分钟上手DeepSeek开发:SpringBoot + Vue2快速构建AI对话系统
人工智能·spring boot·后端
爱吃烤鸡翅的酸菜鱼16 分钟前
Java【网络原理】(2)初识网络续与网络编程
java·网络·后端·java-ee
waicsdn_haha20 分钟前
Eclipse IDE 2025-03 最新版安装教程(官方下载+环境配置详解)
java·linux·开发语言·ide·windows·fpga开发·eclipse
安於宿命24 分钟前
【Linux】管道通信——命名管道
linux·服务器·c++·信息与通信
uhakadotcom27 分钟前
最新发布的Claude 3.7 Sonnet提供了什么新能力,效果如何?
后端·架构·github
Evaporator Core37 分钟前
MATLAB在数据分析和绘图中的应用:从基础到实践
开发语言·matlab·数据分析
YMY哈43 分钟前
计算机网络-面试总结
网络·计算机网络·面试