golang context 简介(3)-广播通知多个业务 go 程退出

2019-09-25 09:20:28 +08:00
 guonaihong

上回聊到,context 是有父子间影响的,父 context cancel 之后,子 context 也会随之 cancel。 这次聊下广播通知多个业务 go 程退出

如何通知多个业务 go 程退出

问题描述

如果在一个业务中会起多个 go 程做一些事,每个 go 程里面会跑流式业务(流式业务可以理解为 dong dong dong 有很多步骤)。你会如何做?

  1. 基于 bool flag 吗?那你要把所有的 select chan 的地方修改成轮训,改动太大,select 多路复用的优点全没了,引入 cpu 空转。关键有时候还不能这么修改

  2. 每次退出的时候发个事件到一个专门的 message chan? 基于计算的方式,万一少发了一个,就会造成 go 程泄漏。而且 message chan 的大小也是一个头疼的地方。

  3. sync.WaitGroup? WaitGroup 只是 go 程同步等待的函数,不是事件通知用的

基于事件通知的做法

在聊之前,大家估计都知道了两个事情,如果一个 chan 被 close 掉,是可以读取多次的,close 掉一个 close 掉的 chan 是会 panic 的。ok,基于这两点认知,我们设计通知多个业务 go 程的退出代码。

```go
func testQuit(failId int, max int) {

    lock := sync.Mutex{}
    wg := sync.WaitGroup{}
    closeOk := false
    done := make(chan struct{})

    wg.Add(max)
    defer wg.Wait()

    for i := 0; i < max; i++ {

        go func(id int) {
            defer wg.Done()

            if id == failId || id == failId-1 {
                lock.Lock()

                if !closeOk {
                    close(done)
                    closeOk = true
                }

                lock.Unlock()
            }   

            select {
            case <-done:
                fmt.Printf("id = %d, quit\n", id) 
            }

        }(i)
    }   
}

我们利用一个 chan 被 close 是可以读取多次的特性,用作广播通知。为了避免一个 chan 会 close 两次会 panic 加入一个 flag 变量。为了 go 程安全,引入 sync.Mutex。

用 context 改造广播通知代码

context 里面实现的代码也是用 close chan 的方式实现的,可能官方看大家都是用这种方式用作广播通知,最后把这种模式加入到了标准库里面

package main

import (
    "context"
    "fmt"
    "sync"
)

func testContext(failId int, max int) {

    var wg sync.WaitGroup

    ctx, cancel := context.WithCancel(context.Background())

    wg.Add(max)
    defer wg.Wait()

    for i := 0; i < max; i++ {

        go func(id int) {
            defer wg.Done()
            if id == failId {
                cancel()
            }

            select {
            case <-ctx.Done():
                fmt.Printf("id = %d, quit\n", id)
            }

        }(i)
    }
}

func main() {
    testContext(1, 5)
    testContext(0, 5)
}

我的 github

https://github.com/guonaihong/gout

2839 次点击
所在节点    Go 编程语言
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/603911

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX