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)
    }
}
相关推荐
L73S37几秒前
C++入门(1)
c++·程序人生·考研·蓝桥杯·学习方法
五味香1 分钟前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
Joeysoda5 分钟前
Java数据结构 (从0构建链表(LinkedList))
java·linux·开发语言·数据结构·windows·链表·1024程序员节
迂幵myself5 分钟前
14-6-1C++的list
开发语言·c++·list
扫地僧0097 分钟前
(Java版本)基于JAVA的网络通讯系统设计与实现-毕业设计
java·开发语言
天乐敲代码8 分钟前
JAVASE入门九脚-集合框架ArrayList,LinkedList,HashSet,TreeSet,迭代
java·开发语言·算法
w(゚Д゚)w吓洗宝宝了32 分钟前
观察者模式 - 观察者模式的应用场景
c++·观察者模式
追Star仙1 小时前
基于Qt中的QAxObject实现指定表格合并数据进行word表格的合并
开发语言·笔记·qt·word
计算机学姐1 小时前
基于微信小程序的民宿预订管理系统
java·vue.js·spring boot·后端·mysql·微信小程序·小程序
DaphneOdera172 小时前
Git Bash 配置 zsh
开发语言·git·bash