context

一、简介

Context用来简化 对于处理单个请求的多个 goroutine 之间与请求域的数据、取消信号、截止时间等相关操作,这些操作可能涉及多个 API 调用。对服务器传入的请求应该创建上下文,而对服务器的传出调用应该接受上下文。它们之间的函数调用链必须传递上下文,或者可以使用 WithCancelWithDeadlineWithTimeoutWithValue创建的派生上下文。当一个上下文被取消时,它派生的所有上下文也被取消。

二、使用示例

package main

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

var wg sync.WaitGroup
func f(ctx context.Context) {
	defer wg.Done()
LOOP:
	for {
		fmt.Println("hello")
		time.Sleep(time.Millisecond * 500)
		select {
		case <-ctx.Done():
			break LOOP
		default:
		}
	}
}

func main(){
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	go f(ctx)
	time.Sleep(time.Second*5)
	cancel()
	wg.Wait()
}

三、接口

type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error
	Value(key interface{}) interface{}
}
接口名 说明
Deadline 返回当前Context被取消的时间,也就是完成工作的截止时间
Done 返回一个Channel,这个Channel会在当前工作完成或者上下文被取消之后关闭,多次调用Done方法会返回同一个Channel
Err 返回当前Context结束的原因,只会在Done返回的Channel被关闭时才会返回非空的值;被取消就会返回Canceled错误;超时就会返回DeadlineExceeded错误
Value Context中返回键对应的值,对于同一个上下文来说,多次调用Value 并传入相同的Key会返回相同的结果,该方法仅用于传递跨API和进程间跟请求域的数据

1. Background()和TODO()

Background() TODO()
返回一个实现了Context接口的background 返回一个实现了Context接口的todo
主要用于main函数、初始化以及测试代码中,作为Context这个树结构的最顶层的Context,也就是根Context 不知道该使用什么Context的时候,可以使用这个
代码中最开始都是以这两个内置的上下文对象作为最顶层的partent context,衍生出更多的子上下文对象,是一个不可取消,没有设置截止时间,没有携带任何值的Context。

2. WithCancel

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

返回两个值,第一个是可撤销的Context值,第二个是用于触发撤销信号的函数。撤销函数被调用后,对应的Context值会先关闭它内部的接收通道,通道关闭了接收该通道的操作就会立即返回,就是Done方法返回的那个通道。然后,它还会向它的所有子值传达撤销信号。这些子值如果还有子值,就会一级一级把撤销信号传递下去。最后,这个Context值会断开它与其父值之间的关联。

3. WithDeadline 与 WithTimeout

func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)

通过调用WithDeadline函数或者WithTimeout函数生成的Context值也是可撤销的。它们不但可以被手动撤销,还会依据在生成是给定的过期时间,自动地进行定时撤销。这里的定时撤销功能是借助它们内部的计时器来实现的。当过期时间到达时,两种Context值的行为与手动撤销是的行为是几乎一致的,只是多了一步停止并释放掉内部的计时器。WithDeadline和WithTimeout是相似的。都是通过设置,会在某个时间自动触发,就是ctx.Done()能够取到值。差别是,DeadLine是设置一个时间点,时间对上了就到期。Timeout是设置一段时间,比如几秒,过个这段时间,就超时。其实底层的Timeout也是通过Deadlin实现的。

4. WithValue

func WithValue(parent Context, key, val interface{}) Context

WithValue函数需要3个参数:父值、键和值。这里键必须是可判断等的,类似字典的键。不过Context值并不是用字典来存储键和值的,而是简单地存储在父值相应的字段中。通过Value方法,可以获取数据。在调用包含属性的Context值的Value方法时会先判断给定的键,如有有就返回存储的值,否则会到其父值中继续查找,会一直沿着上下文根节点的方法一直查找。因为其他几种Context值都是无法携带数据的,所以Value方法在查找的时候,会跨过这这些Context值。

Context接口没有提供改变数据的方法,所以通常只能通过在上下文数中添加含数据的Context值来存储新的数据,或者通过撤销此种值的父值丢弃掉相应的数据。如果存储在这里的数据可以从外部改变,那么必须自信保证安全。

返回值是不可撤销的,撤销信号在传播时,若遇到它们会直接跨过,并试图将信息直接传给它们的子值。

你可能感兴趣的:(golang)