竞争避免:调度器的抢占和主动让出

举报
码乐 发表于 2025/10/11 07:31:36 2025/10/11
【摘要】 1 简介Go 调度器是一个在用户态实现的、基于 G-P-M 模型 的自适应抢占式并发调度系统,让开发者几乎不用关心线程、锁、信号量,而只需专注于任务逻辑。配合通道可以高效实现并发任务。 2 示例 演示抢占调度与主动让出 func cpuIntensive(id int) { for i := 0; i < 10; i++ { fmt.Printf(...

1 简介

Go 调度器是一个在用户态实现的、基于 G-P-M 模型 的自适应抢占式并发调度系统,
让开发者几乎不用关心线程、锁、信号量,而只需专注于任务逻辑。配合通道可以高效实现并发任务。

2 示例 演示抢占调度与主动让出

    func cpuIntensive(id int) {
        for i := 0; i < 10; i++ {
            fmt.Printf("CPU Task %d iteration %d\n", id, i)
            if i == 5 {
                runtime.Gosched() // 主动让出
            }
        }
    }

    func main() {
        go cpuIntensive(1)
        go cpuIntensive(2)
        time.Sleep(time.Second)
    }

说明:

两个 CPU 密集任务交替输出;

即使其中一个长时间运行,也会被抢占或主动让出执行权;

调度器保证了公平与响应性。

  • Go 调度器的优势

    特性 				 描述
    轻量级 			  创建一个 goroutine 只需几 KB 栈空间(线程通常需 MB 级)
    用户态调度   		无需系统调用切换上下文,效率高
    工作窃取    			自动负载均衡
    抢占机制    				避免“饿死”问题
    CSP 模型配合 channel    任务协调更自然、安全
    

3 通道避免竞争

通道是 Go 中内置的并发机制,允许 goroutine 相互💬通信。
它们提供了一种在程序的不同部分之间传递数据的安全方法,防止数据损坏并确保线程安全🔒。这是一个例子:

以下代码显示了如何使用通道将数据从生产者 goroutine 传递到消费者 goroutine,演示了并发通信的强大功能。

    package main

    import "fmt"

    func producer(ch chan int) {
        for i := 1; i <= 5; i++ {
            ch <- i
        }
        close(ch)
    }

    func consumer(ch chan int) {
        for v := range ch {
            fmt.Println(v)
        }
    }

    func main() {
        ch := make(chan int)
        go producer(ch)
        consumer(ch)
    }

Go 的 通道(channel) 是其并发模型的核心,基于“通信顺序进程(CSP, Communicating Sequential Processes)”理论。

通道的核心思想是:不要通过共享内存来通信,而要通过通信来共享内存。
这与传统多线程环境中使用锁、信号量、共享内存等方式有本质区别。

  • 通道典型使用场景(生产者-消费者模型)

示例 1:goroutine 之间的数据传递

    package main

    import (
        "fmt"
        "time"
    )

    func producer(ch chan int) {
        for i := 1; i <= 5; i++ {
            ch <- i // 发送数据
            fmt.Println("Produced:", i)
            time.Sleep(time.Millisecond * 200)
        }
        close(ch) // 通知消费者结束
    }

    func consumer(ch chan int) {
        for num := range ch {
            fmt.Println("Consumed:", num)
        }
    }

    func main() {
        ch := make(chan int)
        go producer(ch)
        consumer(ch)
    }

说明:

producer 向通道发送数据;

consumer 从通道接收数据;

无需显式加锁或信号量,通道天然实现同步。

传统对比:

若用线程 + 共享队列,需要显式 mutex 保护;

用条件变量或信号量协调“数据可用”状态;

Go 通道简化了代码逻辑,避免竞争条件。

【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。