golang context 简介(4)--API 快闪

2019-09-26 09:16:45 +08:00
 guonaihong

上会说到 context.Done()可以通知到多个 go 程的核心代码,下面会对 context 的 API 作个简单介绍

接口原型

type Context interface {
    // 返回 deadline 时间,如果没有设置 ok 为 false,设置为 true
    Deadline() (deadline time.Time, ok bool)

    // 如果 context 被取消 case ctx.Done()会立马返回
    Done() <-chan struct{}

    // 如果 Done 没有被关闭,返回 nil
    // 如果 Done 关闭, 会返回错误, 错误原因如下
    // context 被取消
    // 超时,context 超过截止时间
    // Err() 返回错误,对 Err 的连续调用返回同样的错误
    Err() error

    // 从 context 取出设置的 k/v 变量
    Value(key interface{}) interface{}
}

Deadline 函数的用法

Deadline 函数绑定的 context 如果是 WithTimeout 和 WithDeadline 生成的,ok 变量为 true。如果是 WithCancel 生成的为 false。这点有绕。可以看下面输出

package main

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

func main() {
    // 0
    ctx, _ := context.WithCancel(context.Background())
    fmt.Println(ctx.Deadline())

    // 1
    ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(-time.Second*60))
    fmt.Println(ctx.Deadline())

    // 2
    // 2 和 1 效果一样,只是设置时间方式不一样
    ctx, _ = context.WithTimeout(context.Background(), -time.Second*60)
    fmt.Println(ctx.Deadline())

    // 3
    ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Second*60))
    fmt.Println(ctx.Deadline())

    // 4
    // 4 和 3 效果一样,只是设置时间方式不一样
    ctx, _ = context.WithDeadline(context.Background(), time.Now().Add(time.Second*60))
    fmt.Println(ctx.Deadline())
}

/*输出
0001-01-01 00:00:00 +0000 UTC false
2019-09-26 08:58:43.881587978 +0800 CST m=-59.999921123 true
2019-09-26 08:58:43.881653923 +0800 CST m=-59.999855199 true
2019-09-26 09:00:43.881670028 +0800 CST m=+60.000160905 true

*/

Done 函数

context 控制退出的核心函数。标准库引入 context 虽然褒贬不一,从个人角度来讲觉得挺好,让 case 业务的 chan 都是可取消的,统一了风格。

ctx, cancel := context.WithCancel(context.Background())
defer cancel()
select {
    case <-ctx.Done():
    // case 业务 chan
}

Err 函数

如果 context 被取消,Err 返回错误,多次调用结果一样

package main

import (
    "context"
    "fmt"
)

func main() {

    ctx, cancel := context.WithCancel(context.Background())
    fmt.Println(ctx.Err())
    cancel()
    fmt.Println(ctx.Err())
}

/*
输出
<nil>
context canceled

*/

Value 函数

WithValue 可以向 ctx 里面带一些值,类似 cookie 的感觉。由于不能修改原始 context,只能通过派生一个新的 ctx 携带值。 需要注意的是,context 常见用法是一个 head ctx,派生 n 多的子 ctx,为了避免全局空间污染,都会用自定义类型包装下 key 值


package main

import (
    "context"
    "fmt"
)

type testKey string

func main() {
    // 原始 ctx
    ctx, _ := context.WithCancel(context.Background())

    // 派生一个新的 ctx, 里面包含一个 k,v 数据结构
    ctx = context.WithValue(ctx, testKey("mykey"), "xxxxxxxxxxx")

    f := func(ctx context.Context, k interface{}) {

        if v := ctx.Value(k); v != nil {
            fmt.Printf("value = %s\n", v)
            return
        }
        fmt.Printf("not found value, key = %s\n", k)
    }

    f(ctx, testKey("mykey"))
    f(ctx, "mykey")
}

我的 github

https://github.com/guonaihong/gout

1112 次点击
所在节点    程序员
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/604278

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX