Go语言-Context上下文实践

二郎神六号 · · 687 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

使用 Context 的程序包需要遵循如下的原则来满足接口的一致性以及便于静态分析

1.不要把 Context 存在一个结构体当中,显式地传入函数。Context 变量需要作为第一个参数使用,一般命名为ctx

2.即使方法允许,也不要传入一个 nil 的 Context ,如果你不确定你要用什么 Context 的时候传一个 context.TODO

3.使用 context 的 Value 相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数

4.同样的 Context 可以用来传递到不同的 goroutine 中,Context 在多个goroutine 中是安全的

方法说明

  • Done 方法在Context被取消或超时时返回一个close的channel,close的channel可以作为广播通知,告诉给context相关的函数要停止当前工作然后返回。

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

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

  • Err方法返回context为什么被取消。

  • Deadline返回context何时会超时。

  • Value返回context相关的数据。
  • context 包已经给我们提供了两个,一个是 Background(),一个是 TODO(),这两个函数都会返回一个 Context 的实例。只是返回的这两个实例都是空 Context。BackGound是所有Context的root,不能够被cancel。

实例1

这个例子传递一个上下文,它有一个任意的截止期,它告诉一个阻塞函数,一旦它到达它,它就应该放弃它的工作。

package main

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

func main() {
    d := time.Now().Add(50 * time.Millisecond)
    ctx, cancel := context.WithDeadline(context.Background(),d)
    // Even though ctx will be expired, it is good practice to call its
    // cancelation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()
    select {
        case <- time.After(1 * time.Second):
            fmt.Println("overslept")
        case <- ctx.Done():
            fmt.Println(ctx.Err())
    }
}

运行结果

context deadline exceeded

实例2

package main

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

func main() {
    d := time.Now().Add(50 * time.Second)
    ctx, cancel := context.WithDeadline(context.Background(),d)
    // Even though ctx will be expired, it is good practice to call its
    // cancelation function in any case. Failure to do so may keep the
    // context and its parent alive longer than necessary.
    defer cancel()
    select {
        case <- time.After(1 * time.Second):
            fmt.Println("overslept")
        case <- ctx.Done():
            fmt.Println(ctx.Err())
    }
}

运行结果

overslept

实例3

package main

import (
    "context"
    "fmt"
)

func main() {
    // gen generates integers in a separate goroutine and
    // sends them to the returned channel.
    // The callers of gen need to cancel the context once
    // they are done consuming generated integers not to leak
    // the internal goroutine started by gen.
    gen := func(ctx context.Context) <-chan int {
            dst := make(chan int)
            n := 1
            go func() {
        for {
            select {
                case <-ctx.Done():
                    return // returning not to leak the goroutine
                case dst <- n:
                    n++
            }
        }
            }()

            return dst
    }

    ctx, cancel := context.WithCancel(context.Background())
    defer cancel() // cancel when we are finished consuming integers

    for n := range gen(ctx) {
        fmt.Println(n)
        if n == 5 {
            break
        }
    }
}

运行结果

1
2
3
4
5

实例4

package main

import (
    "fmt"
    "context"
)

func main() {
    type favContextKey string

    f := func(ctx context.Context, k favContextKey) {
        if v := ctx.Value(k); v != nil {
            fmt.Println("found value:", v)
            return
        }
        fmt.Println("key not found:", k)
    }

    k := favContextKey("language")
    ctx := context.WithValue(context.Background(), k, "Go")
    f(ctx, k)
    f(ctx, favContextKey("color"))
}

运行结果

found value: Go
key not found: color

实例5

package main

import (
    "fmt"
    "context"
)

func main() {

    f := func(ctx context.Context, k string) {
        if v := ctx.Value(k); v != nil {
            fmt.Println("found value:", v)
            return
        }
        fmt.Println("key not found:", k)
    }

    ctx := context.WithValue(context.Background(), "language", "Go")
    f(ctx, "language")
    f(ctx, "color")
}

运行结果

found value: Go
key not found: color

有疑问加站长微信联系(非本文作者)

本文来自:51CTO博客

感谢作者:二郎神六号

查看原文:Go语言-Context上下文实践

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

687 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传