context使用

1. 概述

  go语言中goroutine之间的关联关系,缺少维护,在erlang中有专门的机制来保障新开协程的生命周期,在go语言中,只能经过channel + select来实现,但不够直观,很绕。
  Context 一般被译做 上下文 ,它是一个比较抽象的概念,通常理解为程序单元的一个运行状态、现场、快照。上下上下则是存在上下层的传递, 上 会把内容传递给 下 。
  在Go语言中,程序单元也就指的是Goroutine。context 包不只实现了在程序单元之间共享状态变量的方法,同时能经过简单的方法,使咱们在被调用程序单元的外部,经过设置ctx变量值,将过时撤销信号传递给被调用的程序单元。html

  在go服务器中(http服务器),对于每一个请求的request都是在单独的goroutine中进行的,处理一个request也可能涉及多个goroutine之间的交互, 使用context可使开发者方便地在这些goroutine里传递request相关的数据、取消goroutine的signal或截止时间golang

  Done 方法在Context被取消或超时时返回一个close的channel,close的channel能够做为广播通知,告诉给context相关的函数要中止当前工做而后返回。安全

  当一个父operation启动一个goroutine用于子operation,这些子operation不可以取消父operation。下面描述的WithCancel函数提供一种方式能够取消新建立的Context.服务器

  Context能够安全的被多个goroutine使用。开发者能够把一个Context传递给任意多个goroutine而后cancel这个context的时候就可以通知到全部的goroutine。ide

Err方法返回context为何被取消。函数

Deadline返回context什么时候会超时。协程

Value返回context相关的数据。htm

须要注意的就是 调用CancelFunc会取消child以及child生成的context,取出父context对这个child的引用,中止相关的计数器blog

实战:生命周期

  1. context.Background 只应用在最高等级,做为全部派生 context 的根。
  2. context.TODO 应用在不肯定要使用什么的地方,或者当前函数之后会更新以便使用 context。
  3. context 取消是建议性的,这些函数可能须要一些时间来清理和退出。
  4. context.Value 应该不多使用,它不该该被用来传递可选参数。这使得 API 隐式的而且能够引发错误。取而代之的是,这些值应该做为参数传递。
  5. 不要将 context 存储在结构中,要在函数中显式传递它们,最好是做为第一个参数。
  6. 永远不要传递不存在的 context 。相反,若是您不肯定使用什么,使用一个 ToDo context。
  7. Context 结构没有取消方法,由于只有派生 context 的函数才能够取消 context。

参考示例1:

package main

import (
    "context"
    "log"
    "os"
    "time"
)

var logg *log.Logger

func someHandler() {
    ctx, cancel := context.WithCancel(context.Background())
    go doStuff(ctx)

//10秒后取消doStuff
    time.Sleep(10 * time.Second)
    cancel()

}

//每1秒work一下,同时会判断ctx是否被取消了,若是是就退出
func doStuff(ctx context.Context) {
    for {
        time.Sleep(1 * time.Second)
        select {
        case <-ctx.Done():
            logg.Printf("done")
            return
        default:
            logg.Printf("work")
        }
    }
}

func main() {
    logg = log.New(os.Stdout, "", log.Ltime)
    someHandler()
    logg.Printf("down")
        time.Sleep(10 * time.Second)
}

  输出

18:00:17 work
18:00:18 work
18:00:19 work
18:00:20 work
18:00:21 work
18:00:22 work
18:00:23 work
18:00:24 work
18:00:25 work
18:00:26 down
18:00:26 done

  参考示例2:

package main

import (
    "context"
    "log"
    "os"
    "time"
)

var logg *log.Logger

func doTimeOutStuff(ctx context.Context) {
    for {
        time.Sleep(1 * time.Second)

        if deadline, ok := ctx.Deadline(); ok { //设置了deadl
            logg.Printf("deadline set")
            if time.Now().After(deadline) {
                logg.Printf(ctx.Err().Error())
                return
            }

        }

        select {
        case <-ctx.Done():
            logg.Printf("done")
            return
        default:
            logg.Printf("work")
        }
    }
}

func timeoutHandler() {
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    // ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(5*time.Second))
    go doTimeOutStuff(ctx)
    // go doStuff(ctx)

    time.Sleep(10 * time.Second)

    cancel()

}


func main() {
    logg = log.New(os.Stdout, "", log.Ltime)
    timeoutHandler()
    logg.Printf("down")
	time.Sleep(10 * time.Second)
}

  输出:

18:04:37 deadline set
18:04:37 work
18:04:38 deadline set
18:04:38 work
18:04:39 deadline set
18:04:39 work
18:04:40 deadline set
18:04:40 work
18:04:41 deadline set
18:04:41 context deadline exceeded
18:04:46 down

  参考连接:

https://studygolang.com/articles/12566

https://www.cnblogs.com/zhangboyu/p/7456606.html

https://studygolang.com/articles/13866?fr=sidebar

相关文章
相关标签/搜索