golang中Context的使用场景总结

go 1.7中引入 父协程想关闭子协程时,调用其context的cancel函数,即会往其通道中发信号。

Context的功能可以从两个方面去体现:发送终止信号去提供给方法;传递一些公用的数据给多个方法。

Context的主要四种结构,CancelContext,TimeoutContext,DeadLineContext,ValueContext的使用

适用场景有哪些?

由于某种原因(超时,或者强制退出)我们希望中止这个goroutine的计算任务,那么就用得到这个Context了

常见的并发控制组件:waitGroup , Context

具体使用场景:

一、rpc使用 , http请求 。归为一类是超时使用 withCancel。 cancel()会被多次调用,context包的cancel调用是幂等的。可以放心多次调用。异步并行控制, 某一个调用失败,不影响别的调用

代码示例

package main

import (
    "context"
    "fmt"
    "math/rand"
    "net/http"
    "os"
    "time"
)

func main(){
//context控制并发  WithCancel  cancel()
    ctx,cancel := context.WithCancel(context.Background())
   go Worker(ctx,"01")
   go Worker(ctx,"02")
   go Worker(ctx,"03")
    time.Sleep(time.Second * 5)
    fmt.Println("stop all")
    cancel()
    time.Sleep(time.Second * 1)
}

func Worker(ctx context.Context,name string ) {
    for {
        select{
        case <- ctx.Done():
            fmt.Println(name," stop the goroutine!")
            return
        default:
            fmt.Println(name," ing")
            time.Sleep(time.Second *1)
        }
    }
}

//结果打印:
//01  ing
//02  ing
//02  ing
//03  ing
//01  ing
//01  ing
//02  ing
//03  ing
//02  ing
//01  ing
//03  ing
//01  ing
//02  ing
//03  ing
//stop all
//02  stop the goroutine!
//03  stop the goroutine!
//01  stop the goroutine!

context WithCancel 取消控制 发送取消信号,保证自己这个逻辑中发散出去的goroutine全部成功取消

二、HTTP服务器的request互相传递数据 WithValue。 WithContext 常用于拦截器,cookie验证后,把公用信息存储到request中,接口自行去取

代码示例

// WithValue 使用
func main(){
    ContextWithValue()
}

func ContextWithValue() {
    rand.Seed(time.Now().Unix())
    ctx := context.WithValue(context.Background(), keyID, rand.Int())
    operation1(ctx)
}

func operation1(ctx context.Context) {
    fmt.Println("operation1 for id:", ctx.Value(keyID), " completed")
    operation2(ctx)
}

func operation2(ctx context.Context) {
    fmt.Println("operation2 for id:", ctx.Value(keyID), " completed")
}

三、超时请求。 rpc调用 http调用 WithTimeout WithDeadLine(文件io或者网络io等耗时操作可以查看剩余的时间是否充足,,决定是否进行下一步操作)

WithDeadLine 与withTimeout传入的时间参数不一样,其余一样.

WithTimeout代码示例

import (
    "context"
    "fmt"
    "math/rand"
    "net/http"
    "os"
    "time"
)


// WithTimeout
func main(){
// 创建一个空ctx
    ctx := context.Background()
    // 设置超时时间
    cxt, _ := context.WithTimeout(ctx, 1*time.Millisecond)

    start := time.Now()
    // 创建一个请求 访问谷歌
    req, _ := http.NewRequest(http.MethodGet, "http://baidu.com", nil)
    // 将带有取消方法的ctx和请求关联
    req = req.WithContext(cxt)

    client := &http.Client{}
    res, err := client.Do(req)
    if err!=nil{
        fmt.Println("Request failed:", err ," time ",time.Now().Sub(start))

        return
    }
    fmt.Println("Response received, status code:", res.StatusCode)
}
WithTimeout代码示例
func main(){
    ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Second * 4))
    defer cancel()
    copyFileWithDeadline(ctx)
    time.Sleep(time.Second * 5)
}

func copyFileWithDeadline(ctx context.Context)  {
    deadline, ok := ctx.Deadline()
    if ok == false {
        return
    }
    // deadline.Sub(time.Now()) 截止时间与当前时间的差值
    isEnough := deadline.Sub(time.Now()) > time.Second * 5
    if isEnough {
        fmt.Println("copy file")
    } else {
        fmt.Println("isEnough is false return")
        return
    }
}

Context控制并发代码示例,注意协程执行顺序是随机的。但waitgroup是并发控制是可以保证顺序执行

package main

import (
    "context"
    "fmt"
    "math/rand"
    "net/http"
    "os"
    "time"
)

func main(){
//context控制并发  WithCancel  cancel()
    ctx,cancel := context.WithCancel(context.Background())
   go Worker(ctx,"01")
   go Worker(ctx,"02")
   go Worker(ctx,"03")
    time.Sleep(time.Second * 5)
    fmt.Println("stop all")
    cancel()
    time.Sleep(time.Second * 1)
}

func Worker(ctx context.Context,name string ) {
    for {
        select{
        case <- ctx.Done():
            fmt.Println(name," stop the goroutine!")
            return
        default:
            fmt.Println(name," ing")
            time.Sleep(time.Second *1)
        }
    }
}

//结果打印:
//01  ing
//02  ing
//02  ing
//03  ing
//01  ing
//01  ing
//02  ing
//03  ing
//02  ing
//01  ing
//03  ing
//01  ing
//02  ing
//03  ing
//stop all
//02  stop the goroutine!
//03  stop the goroutine!
//01  stop the goroutine!

func main(){
// waitgroup来控制并发
    wg := sync.WaitGroup{}
    wg.Add(2)
    go func(){
        fmt.Println("1 job")
        wg.Done()
    }()
    go func(){
        fmt.Println(" 2 job")
        wg.Done()
    }()
    wg.Wait()
    fmt.Println(" end")
}

最后总结:

1. 值传递。 --值传递只是context的一个辅助功能,常见使用:cookie,拦截器或者日志信息、调试信息记录

2. 超时控制 --http设置超时时间,rpc调用,--文件io或者网络io等耗时操作可以查看剩余的时间是否充足,决定是否进行下一步操作

3. 取消控制 --发送取消信号,保证自己这个逻辑中发散出去的goroutine全部成功取消

猜你喜欢

转载自blog.csdn.net/xia_2017/article/details/128821215