go context

437 阅读2分钟

引言

1、WaitGroup,它是一种控制并发的方式,它的这种方式是控制多个goroutine同时完成。
func main() {
    var wg sync.WaitGroup
    wg.Add(2)
    go func() {
        time.Sleep(2*time.Second)
        fmt.Println("1号完成")
        wg.Done()
    }()
    go func() {
        time.Sleep(2*time.Second)
        fmt.Println("2号完成")
        wg.Done()
    }()
    wg.Wait()
    fmt.Println("好了,大家都干完了,放工")
2、chan通知

我们都知道一个goroutine启动后,我们是无法控制他的,大部分情况是等待它自己结束,那么如果这个goroutine是一个不会自己结束的后台goroutine呢?比如监控等,会一直运行的。这种情况化,一直傻瓜式的办法是全局变量,其他地方通过修改这个变量完成结束通知,然后后台goroutine不停的检查这个变量,如果发现被通知关闭了,就自我结束。这种方式也可以,但是首先我们要保证这个变量在多线程下的安全,基于此,有一种更好的方式:chan + select

func main() {
    stop := make(chan bool)
    go func() {
        for {
            select {
            case <-stop:
                fmt.Println("监控退出,停止了...")
                return
            default:
                fmt.Println("goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }()
    time.Sleep(10 * time.Second)
    fmt.Println("可以了,通知监控停止")
    stop<- true
    //为了检测监控过是否停止,如果没有监控输出,就表示停止了
    time.Sleep(5 * time.Second)

Go context

一、步骤

1、生成一个context(链),有两种:context.WithTimeout超时取消和context.WithCancel手动取消

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

2、然后用把ctx传到某个goroutine A中,这个A可以通过select + ctx.Done()判断是否该ctx结束了

for {
    select {
    case <-ctx.Done():
        fmt.Println("监控退出,停止了...")
        return
    default:
        fmt.Println("goroutine监控中...")
        time.Sleep(2 * time.Second)
    }
}

3、动作,产生一个结束终结的动作

//如果该ctx是WithCancel生成的,则需要手动执行:
cancel()


//如果该ctx是WithTimeout生成的,则不需要手动,超时就自动执行了

二、举例代码

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    go func(ctx context.Context) {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("监控退出,停止了...")
                return
            default:
                fmt.Println("goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }(ctx)

    time.Sleep(10 * time.Second)
    fmt.Println("可以了,通知监控停止")
    
    // 如果为ctx, _ := context.WithTimeout(context.Background(), timeout),timeout为时间,则不需要执行cancel
    cancel()
    
    //为了检测监控过是否停止,如果没有监控输出,就表示停止了
    time.Sleep(5 * time.Second)
}