Golang——Context

context.Background() Context

这个函数返回一个空 context。这只适用于高等级(在 main 或顶级请求处理中)。这能用于派生我们稍后谈及的其他 context 。

ctx := context.Background()

context.TODO() Context

这个函数也是创建一个空 context。也只能用于高等级或当您不确定使用什么 context,或函数以后会更新以便接收一个 context 。这意味您(或维护者)计划将来要添加 context 到函数。

ctx := context.TODO()

context.WithValue(parent Context, key, val interface{}) (ctx Context, cancel CancelFunc)

此函数接收 context 并返回派生 context,其中值 val 与 key 关联,并通过 context 树与 context 一起传递。这意味着一旦获得带有值的 context,从中派生的任何 context 都会获得此值。

ctx := context.WithValue(context.Background(), "key", "value")

context.WithCancel(parent Context) (ctx Context, cancel CancelFunc)

这是它开始变得有趣的地方。此函数创建从传入的父 context 派生的新 context。

返回派生 context 和取消函数。只有创建它的函数才能调用取消函数来取消此 context。如果您愿意,可以传递取消函数,但是,强烈建议不要这样做。这可能导致取消函数的调用者没有意识到取消 context 的下游影响。可能存在源自此 context 的其他 context,这可能导致程序以意外的方式运行。简而言之,永远不要传递取消函数。

ctx, cancel := context.WithCancel(context.Background()) // 不要传递 cancel 给其他函数

context.WithTimeout(parent Context, timeout time.Duration) (ctx Context, cancel CancelFunc)

此函数类似于 context.WithDeadline。不同之处在于它将持续时间作为参数输入而不是时间对象。此函数返回派生 context,如果调用取消函数或超出超时持续时间,则会取消该派生 context。

ctx, cancel := context.WithTimeout(context.Background(), 2 * time.Second) // 同样不要传递 cancel

父子 context

package main

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

func main() {
    ch := make(chan bool, 1) // ch can close main
    ctx, cancel := context.WithCancel(context.Background())

    go func() {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("main: case ctx.Done!!!")
                return
            case <-ch:
                cancel()
            }
        }
    }()

    doSomething(ctx, ch)
    time.Sleep(100 * time.Millisecond) // 睡眠100ms 为了让main函数不立即结束
}

func doSomething(ctx context.Context, ch chan bool) {
    localCtx, _ := context.WithCancel(ctx)

    // do...

    // if something wrong need to close main
    ch <- true

    go func() {
        select {
        case <-localCtx.Done():
            fmt.Println("doSomething: case ctx.Done!!!")
        }
    }()
}

输出

doSomething: case ctx.Done!!!
main: case ctx.Done!!!

说明父 context 可以 cancel 掉子 context

package main

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

func main() {
    ch := make(chan bool, 1) // ch can close main
    ctx, cancel := context.WithCancel(context.Background())

    go func() {
        for {
            select {
            case <-ctx.Done():
                fmt.Println("main: case ctx.Done!!!")
                return
            case <-ch:
                cancel()
            }
        }
    }()

    doSomething(ctx)
    time.Sleep(100 * time.Millisecond) // 睡眠100ms 为了让main函数不立即结束
}

func doSomething(ctx context.Context) {
    localCtx, cancel := context.WithCancel(ctx)

    // do...

    // if something wrong need to close main
    cancel()

    go func() {
        select {
        case <-localCtx.Done():
            fmt.Println("doSomething: case ctx.Done!!!")
        }
    }()
}

输出

doSomething: case ctx.Done!!!

说明: 子 context 不可以 cancel 父 context

超时请求

package main

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

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
    defer cancel()

    select {
    case <-time.After(1 * time.Second):
        fmt.Println("overslept")
    case <-ctx.Done():
        fmt.Println(ctx.Err()) // prints "context deadline exceeded"
    }
}
全部评论

相关推荐

有趣的牛油果开挂了:最近这个阶段收到些杂七杂八的短信是真的烦
点赞 评论 收藏
分享
听说改名字就能收到offer哈:Radis写错了兄弟
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务