Go 编程: 如何实现协程调度的精准控制
说起 Go 协程的调度,如果你了解 Go 调度器以及其实现的 G/P/M 模型,当然有助于应用的开发。但是在应用层面上,这些底层的调度原理并不会帮你太多,实现 Go 协程的精准调度得完全靠自己。
问题
例如,实际应用中我们要解决的问题是这样的。那么,我们该如何实现调度的精准控制呢?

实现
简单的协程控制,很多人都会使用 sync.WaitGroup 进行多协程的控制。但是 sync.WaitGroup 面对图示的调度控制就显得无能为力了。
上图的协程调度看似复杂,如果仔细分析一下,就会发现单个协程的启动需要等待依赖协程完成才能开始。所以,问题的关键是实现协程间的前置依赖。
那么,这样一个前置依赖该如何实现呢?这里直接贴出我的方案,你也可以想想你要如何实现。
package event
import (
 "sync"
 "sync/atomic"
)
type Event struct {
 fired int32
 c     chan struct{}
 once  sync.Once
}
func New() *Event {
 return &Event{c: make(chan struct{})}
}
func (ev *Event) Fire() int32 {
 atomic.AddInt32(&ev.fired, 1)
 ev.once.Do(func() {
  close(ev.c)
 })
 return ev.fired
}
func (ev *Event) Done() <-chan struct{} {
 return ev.c
}
func (ev *Event) HasFired() bool {
 return atomic.LoadInt32(&ev.fired) > 0
}
这样一个简单的事件触发器就可以来模拟协程前置依赖。例如,图示中的B1的启动依赖于A1与C1,那么我们的实现代码就可以这样实现:
package main
import (
    "github.com/x-mod/event" //Event 开源项目在这里
)
func main(){
    //前置依赖
    a1 := event.New()
    b1 := event.New()
    c1 := event.New()
    //A1 协程
    go func(){
        defer a1.Fire()
        // A1 LOGIC ...
    }
    //B1 协程
    go func(){
        defer b1.Fire()
        
        // 等待前置依赖完成
        <-a1.Done()
        <-c1.Done()
        // B1 LOGIC ...
    }
    //C1 协程
    go func(){
        defer c1.Fire()
        // C1 LOGIC ...
    }
}项目地址:https://github.com/x-mod/event
推荐阅读
站长 polarisxu
自己的原创文章
不限于 Go 技术
职场和创业经验
Go语言中文网
每天为你
分享 Go 知识
Go爱好者值得关注
评论
