Go 编程: 如何实现协程调度的精准控制

共 1115字,需浏览 3分钟

 ·

2020-07-27 19:48

说起 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的启动依赖于A1C1,那么我们的实现代码就可以这样实现:

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




推荐阅读



学习交流 Go 语言,扫码回复「进群」即可


站长 polarisxu

自己的原创文章

不限于 Go 技术

职场和创业经验


Go语言中文网

每天为你

分享 Go 知识

Go爱好者值得关注



浏览 55
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报