zoukankan      html  css  js  c++  java
  • Go Context

      Context通常被译作上下文,它是一个比较抽象的概念。在讨论链式调用技术时也经常会提到上下文。一般理解为程序单元的一个运行状态、现场、快照,而翻译中上下又很好地诠释了其本质,上下则是存在上下层的传递,上会把内容传递给下。在Go语言中,程序单元也就指的是Goroutine。

    在Go语言中控制并发有两种经典的方式,一种是WaitGroup,另外一种就是Context。

    Context业务场景

    每个Goroutine在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个Context变量中,传递给要执行的Goroutine中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。在网络编程下,当接收到一个网络请求Request,在处理这个Request的goroutine中,可能需要在当前gorutine继续开启多个新的Goroutine来获取数据与逻辑处理(例如访问数据库、RPC服务等),即一个请求Request,会需要多个Goroutine中处理。而这些Goroutine可能需要共享Request的一些信息;同时当Request被取消或者超时的时候,所有从这个Request创建的所有Goroutine也应该被结束。

    初识Context

    上面说的这种场景是存在的,比如一个网络请求Request,每个Request都需要开启一个goroutine做一些事情,这些goroutine又可能会开启其他的goroutine。所以我们需要一种可以跟踪goroutine的方案,才可以达到控制他们的目的,这就是Go语言为我们提供的Context,称之为上下文非常贴切,它就是goroutine的上下文。

    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("可以了,通知监控停止")
        cancel()
        //为了检测监控过是否停止,如果没有监控输出,就表示停止了
        time.Sleep(5 * time.Second)
    
    }

    Context with用法

    context.WithValue()  设置值传递

    context.WithCancel()  设置关闭Groutine

    context.WithTimeout()  设置超时时间

    context.WithDeadine()   设置绝对过期时间

    context.TODO()  返回一个非nil的空Context,当不清楚使用哪种Context或尚不可用时,应该使用context.TODO 

    context.Backgroup()  返回一个非nil的空Context,也是一个根Context(top-level Context)

    // Background returns a non-nil, empty Context. It is never canceled, has no
    // values, and has no deadline. It is typically used by the main function,
    // initialization, and tests, and as the top-level Context for incoming
    // requests.
    func Background() Context {
    return background
    }

    Context控制多个goroutine

    使用Context控制一个goroutine的例子如上,非常简单,下面我们看看控制多个goroutine的例子,其实也比较简单。

    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        go watch(ctx,"【监控1】")
        go watch(ctx,"【监控2】")
        go watch(ctx,"【监控3】")
    
        time.Sleep(10 * time.Second)
        fmt.Println("可以了,通知监控停止")
        cancel()
        //为了检测监控过是否停止,如果没有监控输出,就表示停止了
        time.Sleep(5 * time.Second)
    }
    
    func watch(ctx context.Context, name string) {
        for {
            select {
            case <-ctx.Done():
                fmt.Println(name,"监控退出,停止了...")
                return
            default:
                fmt.Println(name,"goroutine监控中...")
                time.Sleep(2 * time.Second)
            }
        }
    }

    示例中启动了3个监控goroutine进行不断的监控,每一个都使用了Context进行跟踪,当我们使用cancel函数通知取消时,这3个goroutine都会被结束。这就是Context的控制能力,它就像一个控制器一样,按下开关后,所有基于这个Context或者衍生的子Context都会收到通知,这时就可以进行清理操作了,最终释放goroutine,这就优雅的解决了goroutine启动后不可控的问题。

    Context传递消息

    package main
    
    import (
        "context"
        "fmt"
        "time"
        "sync"
    )
    
    func worker(ctx context.Context, wg sync.WaitGroup) {
        traceCode, ok := ctx.Value("TRACE_CODE").(string)
        if ok {
            fmt.Printf("trace_code:%s
    ", traceCode)
        }
    
        for {
            fmt.Printf("worker, trace_code:%s
    ", traceCode)
            select {
            case <- ctx.Done():
                return
            default:
                time.Sleep(time.Second)
            }
        }
        wg.Done()
    
    }
    
    func main() {
        ctx, cancel := context.WithCancel(context.Background())
        ctx = context.WithValue(ctx, "TRACE_CODE", "12345678")
        var waitGroup sync.WaitGroup
    
        go worker(ctx, waitGroup)
        time.Sleep(3 * time.Second)
        cancel()
    
        waitGroup.Wait()
    }

    转载至: 

    http://www.flysnow.org/2017/05/12/go-in-action-go-context.html

    https://studygolang.com/articles/13343

  • 相关阅读:
    命令
    碎片知识
    驱动问题
    网络基础知识普及
    面向设计大作业——公司餐厅
    面向对象--购物车
    OO之接口-DAO模式代码阅读及应用
    有理数类的定义
    图知识点总结
    Java课程设计总结
  • 原文地址:https://www.cnblogs.com/vincenshen/p/9496717.html
Copyright © 2011-2022 走看看