Go 是协作式调度,还是抢占式调度? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Jianzs
V2EX    Go 编程语言

Go 是协作式调度,还是抢占式调度?

  •  
  •   Jianzs
    pluto-lang 2023-03-28 11:15:51 +08:00 5019 次点击
    这是一个创建于 931 天前的主题,其中的信息可能已经有所发展或是发生改变。

    首先,介绍我对 GoLang 调度过程的认识,如果有误,请指出。

    GoLang 的调度模型是 GMP 调度模型,调度的计算实体是 Goroutine ,调度的资源载体是 Machine (线程)。用户创建的 Goroutine 会被提交至 Processor ,Processor 会根据当前空闲与否决定将 Goroutine 是否调度到一个线程执行,当 Processor 上当前线程执行的 Goroutine 结束或挂起时,则会调度新的 Goroutine 至某线程。

    接下来,介绍我的疑惑:

    • 一方面,当协程退出或 IO 阻塞时主动让出占用的计算资源,供其他 Goroutine 使用,这是典型的协作式调度。
    • 另一方面,当一个协程长时间占用计算资源,runtime 会将其强制中断,进而让其他 Goroutine 都能有机会得到执行,这应该是抢占式调度。

    从以上两方面,感觉 Go 既有协作式?又有抢占式?但是为什么大家都说 Go 是协作式呢?

    站内大佬颇多,还请解惑~

    21 条回复    2023-03-28 21:05:17 +08:00
    emSaVya
        1
    emSaVya  
       2023-03-28 11:26:24 +08:00
    印象里 go 1.14 有一个 update 。转向 preemptive
    centralpark
        2
    centralpark  
       2023-03-28 11:30:08 +08:00   6
    你感觉是对的呀,相对传统线程,goroutine 有协作式调度;相对传统 coroutine ,goroutine 也可以抢占式调度。

    这不就跟某知名运动员一样,在中国是中国人,在美国是美国人。

    大家都说他是协作式,那是因为传统的并发方式是多进程和多线程,相对他们而言的。
    AugOmin
        3
    AugOmin  
       2023-03-28 11:38:54 +08:00
    都有
    zeonll
        4
    zeonll  
       2023-03-28 11:56:35 +08:00
    协作式调度是在后面的版本才加进来的吧。(加入了协作调度功能,被营销号吹成 转成协作调度)
    之前的版本都是抢占,所以两种方式现在都有。
    leonshaw
        5
    leonshaw  
       2023-03-28 13:10:25 +08:00 via Android
    对 goroutine 来说应该是抢占式,IO 调用并没有主动让出的意思,而是被 runtime 插入的调度点。Gosched()才算协作。
    Kisesy
        6
    Kisesy &nbp;
       2023-03-28 13:25:11 +08:00
    因为 go 会不断更新啊,你参考的信息又不一定是最新的
    rockyliang
        7
    rockyliang  
       2023-03-28 13:25:20 +08:00   1
    @zeonll 你是说反了吗,应该是老版本是协作式调度,后面才加入的抢占式调度
    DarkCat123
        8
    DarkCat123  
       2023-03-28 13:33:27 +08:00
    > Goroutines are now asynchronously preemptible. As a result, loops without function calls no longer potentially deadlock the scheduler or significantly delay garbage collection. ( https://go.dev/doc/go1.14)


    @leonshaw Goroutine 现在会在编译的时候被编译器插入类似 Gosched() (实际是用 semaphore )
    zoharSoul
        9
    zoharSoul  
       2023-03-28 13:40:06 +08:00
    大家都说 go 的是抢占式吧 相对于别的协程
    DiffView
        10
    DiffView  
       2023-03-28 13:45:45 +08:00   3
    你对于 GoLang 的调度过程和 GMP 调度模型的理解基本上是正确的。

    关于你的疑惑,GoLang 的调度模型的确是协作式调度模型。虽然在某些情况下,GoLang 会使用抢占式调度来强制中断长时间占用计算资源的 Goroutine ,但这并不代表整个调度模型是抢占式的。

    在 GoLang 的调度模型中,每个 Goroutine 都有一个 Goroutine 栈和调度器关联。当 Goroutine 遇到阻塞或等待 IO 时,调度器会将该 Goroutine 标记为不可运行,让出 CPU 资源给其他可运行的 Goroutine 。当 IO 操作完成或其他等待条件满足时,该 Goroutine 再次标记为可运行,调度器会在适当的时候重新调度该 Goroutine 。

    同时,GoLang 的调度器会根据一定的规则,例如时间片轮转算法和休眠唤醒机制等,在运行中的 Goroutine 之间进行切换,保证每个 Goroutine 都能得到执行机会。这种协作式调度模型能够更好地利用 CPU 资源,避免了不必要的上下文切换开销,也避免了抢占式调度可能带来的问题,例如死锁和资源争用等。

    综上所述,尽管 GoLang 的调度模型在某些情况下会使用抢占式调度,但整个调度模型仍然是协作式调度模型。因此,大家通常会将 GoLang 的调度模型称为协作式调度模型。
    8355
        11
    8355  
       2023-03-28 13:52:54 +08:00   1
    我帮你问了 chatgpt4

    你对 GoLang 调度过程的认识基本正确。的确,Go 语言的调度模型是 GMP 模型,涉及到 Goroutine ( G )、Processor ( P )和 Machine ( M )三个主要概念。

    关于你的疑惑,Go 语言的调度机制主要是协作式调度。然而,从 Go 1.14 版本开始,Go 在协作式调度的基础上加入了一些抢占式调度的特性。这主要体现在 goroutine 抢占方面,通过在执行过程中插入抢占点(例如函数调用)来实现。这样,当一个 Goroutine 长时间占用计算资源时,runtime 可以强制中断它,让其他 Goroutine 有机会得到执行。

    尽管 Go 语言引入了这些抢占式调度特性,但它仍然主要是协作式调度。这是因为 Goroutine 在许多情况下主动让出计算资源(例如,当遇到 IO 阻塞时),这是协作式调度的核心特点。而抢占式调度特性只是在特定情况下作为补充,以确保在某些场景下调度的公平性和响应性。

    因此,大家通常认为 Go 语言主要采用协作式调度,而抢占式调度仅在特定情况下作为辅助。这也是为什么大家说 Go 是协作式调度的原因。
    8355
        12
    8355  
       2023-03-28 13:53:48 +08:00
    @DiffView #10 这个应该也是 gpt 的回答 哈哈
    DiffView
        13
    DiffView  
       2023-03-28 13:54:43 +08:00
    @8355 笑死,因为我也想知道答案,但是网上根本没有答案
    GopherDaily
        14
    GopherDaily  
       2023-03-28 13:58:38 +08:00
    协作式,但是为了一些场景一直不让出控制权,后续加入了一定的抢占的场景
    centralpark
        15
    centralpark  
       2023-03-28 14:17:24 +08:00
    @DiffView @8355 你俩是真不怕封号……
    DiffView
        16
    DiffView  
       2023-03-28 14:21:07 +08:00
    这里我比较有兴趣,后面也查了下资料

    从 go 1.14 开始,go 调度器是非合作抢占的。每个 goroutine 在一定的时间片后被抢占。在 go 1.19.1 中是 10ms 。
    代码在这里: https://github.com/golang/go/blob/go1.19.1/src/runtime/proc.go#L5279-L5281
    zmcity
        17
    zmcity  
       2023-03-28 14:36:27 +08:00
    混合式。

    但 golang 是以能方便的写协作调度出名的。
    8355
        18
    8355  
       2023-03-28 14:54:08 +08:00
    @centralpark #15 帮 op 问一问只回一个 不是机器人自动回啊。
    leonshaw
        19
    leonshaw  
       2023-03-28 15:51:15 +08:00   1
    @DarkCat123 “协作式”调度时,指的是谁在协作?应该是指用户协程,而不是 runtie. 当一个 goroutine 陷入 IO 调用或者 prologue ,它本身是没有让出计算资源的主观意愿的,这一点可以对比显式的 Gosched() 调用和其它语言的 await. 同样也可以类比系统线程,在系统调用返回前,计算资源可能被释放并切换到其它线程,这应该也不算协作,因为线程并没有想要释放资源,是操作系统的决策。
    jdz
        20
    jdz  
       2023-03-28 20:03:17 +08:00 via Android
    协作式效率高,协程抢占式没意义
    bruce0
        21
    bruce0  
       2023-03-28 21:05:17 +08:00
    @jdz 我记得抢占式是有意义的, 协作式是在函数调用时,检查是否让出执行, 就会出现一种情况

    ```
    func main() {
    runtime.GOMAXPROCS(1)
    go func() {
    for {
    }
    }()
    time.Sleep(time.Millisecond)
    println("OK")
    }
    ```
    这段代码在 go1.14 之前不会打印 OK 的 就是因为协程中没有函数调用,也就没有协作式的检查点, 协程就不会退出
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     3519 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 10:38 PVG 18:38 LAX 03:38 JFK 06:38
    Do have faith in what you're doing.
    ubao snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86