【Golang】Golang Context上下文包

context介绍

  Golang里的Context包非常重要,Context包是在golang 1.7之后引入的,它主要是解决如何优雅的去控制子goroutine退出。

 

控制并发的两种方式

  1. Waitgroup:

   适用于多个goroutine执行同一件事,然后一直等到所有的goroutine执行完成,并且阻塞主线程的执行,直到所有的goroutine执行完成。比如将一个大的任务拆解多个子任务。

package main

import (
   "fmt"
   "sync"
   "time"
)

func main()  {
    var wg sync.WaitGroup
    wg.Add(2)
    //job 1
    go func(){
        //暂停2秒
        time.Sleep(2 * time.Second)
        fmt.Println("job 1 done")
        wg.Done()//上面执行完毕后就会执行done
    }()

    //job 2
    go func(){
        //暂停1秒
        time.Sleep(1 * time.Second)
        fmt.Println("job 2 done")
        wg.Done()
    }()

    wg.Wait()//wait一直在阻塞直到等待所有job都完成
    fmt.Println("All Done")
}

  执行结果

songguojundeMacBook-Pro:context songguojun$ go run main.go 
job 2 done
job 1 done
All Done

   上面的例子每个job都是自己跑直到跑完任务结束,如果外界要中断其执行,看看下面的例子。

package main

import (
   "fmt"
   "time"
   "sync"
)

var wg sync.WaitGroup
var quitOut bool

func worker() {
    defer wg.Done()
    //死循环
    for {
        fmt.Println("for running.")
        time.Sleep(1 * time.Second)
        if quitOut {//达到条件就退出
            break
        }
    }
}

func main()  {
    wg.Add(1)
    go worker()
    time.Sleep(5 * time.Second)
    //设置quitOut为true通知子goroutine退出
    quitOut = true
    wg.Wait()
    fmt.Println("running closed")
}

执行结果

songguojundeMacBook-Pro:context songguojun$ go run main.go 
for running.
for running.
for running.
for running.
for running.
running closed

 下面的例子使用的是channel + select的方式。

package main

import (
   "fmt"
   "time"
)

func main()  {
    stop := make(chan bool) //chan初始化
    go func() {
        for {
            select {
                //接受信息
            case <-stop:
                fmt.Println("got the stop channel")
                return    //这里如果写break只能跳出select而不能跳出for循环
            default:
                fmt.Println("still working")
                time.Sleep(1 * time.Second)
            }
        }
    }()
    
    time.Sleep(5 * time.Second)
    fmt.Println("stop the gorutine")
    //将true传递给stop
    stop <- true
    time.Sleep(5 * time.Second)
}

执行结果

songguojundeMacBook-Pro:context songguojun$ go run main.go 
still working
still working
still working
still working
still working
stop the gorutine
got the stop channel    #下面没有输出,就是已经结束gorutine了

 

  2. Context

    Context用于在一个主gorutine去通知子gorutine结束工作,上面的代码例子中,如果gorutine里嵌套gorutine,就是一个job中又包含一个job,这时候这种场景就需要用到Context

package main

import (
   "fmt"
   "time"
   "context"
)

func worker(ctx context.Context, name string) {
    go func() {
        for {
            select {
            case <-ctx.Done():   //Done是一个只读的chan
                fmt.Println(name, "got the stop channel")
                return
            default:
                fmt.Println(name, "still working")
                time.Sleep(1 * time.Second)
            }
        }
    }()
}

func main()  {
    ctx, cancel := context.WithCancel(context.Background())  //context.WithCancel表示创建一个取消函数

    go worker(ctx, "node1")
    go worker(ctx, "node2")
    go worker(ctx, "node3")

    time.Sleep(5 * time.Second)
    fmt.Println("stop the gorutine")
    //cancel方法可以将上面node1 node2 node3都停止掉
    cancel()
    time.Sleep(5 * time.Second)
}

执行结果

songguojundeMacBook-Pro:context songguojun$ go run main.go 
node3 still working
node1 still working
node2 still working
node2 still working
node1 still working
node3 still working
node2 still working
node1 still working
node3 still working
node3 still working
node1 still working
node2 still working
node2 still working
node3 still working
node1 still working
stop the gorutine
node1 got the stop channel
node2 got the stop channel
node3 got the stop channel

 

posted @ 2019-05-30 13:32  songguojun  阅读(339)  评论(0编辑  收藏  举报