Go基础学习-Context

  • Go中常用的并发模型之一;(一共有三种:通过channel通知实现并发控制;通过sync包中的WaitGroup实现并发控制;Context上下文,实现并发控制)

  • context对goroutine进行跟踪,从而达到控制他们的目的

  • context接口不需要实现,其内置了两个方法帮我们实现了:

       var (
       background = new(emptyCtx)
       todo       = new(emptyCtx)
       )
    
       func Background() Context {
       	return background
       }
       
       func TODO() Context {
       	return todo
       }
    

    Background,主要用于main函数、初始化以及测试代码中,作为Context这个树结构的最顶层的Context,也就是根Context。和todo本质都是emptyCtx,不可取消;不携带任何的值

  • Context的接口主要有四个;

    type Context interface {
        Deadline() (deadline time.Time, ok bool)
        Done() <-chan struct{}
        Err() error
        Value(key interface{}) interface{}
    }
    

    Deadline方法:是获取设置的截止时间的意思,第一个返回式是截止时间,到了这个时间点,Context会自动发起取消请求;第二个返回值ok==false时表示没有设置截止时间,如果需要取消的话,需要调用取消函数进行取消。
    Done方法返回一个只读的chan,类型为struct{},我们在goroutine中,如果该方法返回的chan可以读取,则意味着parent context已经发起了取消请求,我们通过Done方法收到这个信号后,就应该做清理操作,然后退出goroutine,释放资源。
    Err方法返回取消的错误原因,因为什么Context被取消。
    Value方法获取该Context上绑定的值,是一个键值对,所以要通过一个Key才可以获取对应的值,这个值一般是线程安全的。

  • 子Context的继承衍生主要有

    func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
    func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
    func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
    func WithValue(parent Context, key, val interface{}) Context
    

    他们都传递一个父Context作为参数;返回子Context
    WithCancel函数,返回子Context,以及一个取消函数用来取消Context。
    WithDeadline函数,和WithCancel差不多,它会多传递一个截止时间参数,意味着到了这个时间点,会自动取消Context,当然我们也可以不等到这个时候,可以提前通过取消函数进行取消。

    WithTimeoutWithDeadline基本上一样,这个表示是超时自动取消,是多少时间后自动取消Context的意思。

    WithValue函数和取消Context无关,它是为了生成一个绑定了一个键值对数据的Context,这个绑定的数据可以通过Context.Value方法访问到

func f2(ctx context.Context) {
LOOP:
	for {
		fmt.Println("worder 2")
		time.Sleep(time.Millisecond * 200)
		select {
		case <-ctx.Done(): // 等待上级通知
			break LOOP
		default:
		}
	}
}

func f(ctx context.Context) {
	go f2(ctx)
LOOP:
	for {
		fmt.Println("worder 1")
		time.Sleep(time.Millisecond * 200)
		select {
		case <-ctx.Done(): // 等待上级通知
			break LOOP
		default:
		}
	}
	defer wg.Done()
}
func main() {
	ctx, cancel := context.WithCancel(context.Background())
	wg.Add(1)
	go f(ctx)
	time.Sleep(time.Second * 2)
	cancel() //通知子goroutine结束
	wg.Wait()
}
//withDeadline()
func main(){
	d := time.Now().Add(time.Second * 1)
		ctx2, cancel2 := context.WithDeadline(context.Background(), d)
		//尽管ctx2会过期,但在任何情况下调用它的cancel2函数都是很好的实践。
		//如果不这样做,可能会使上下文及其父类存活的时间超过必要的时间。
		defer cancel2()
		select {
		case <-ctx2.Done():
			fmt.Println(ctx2.Err())
		case <-time.After(time.Millisecond * 2000):
			fmt.Println("working...")
		}
}
//WithTimeout()通常用于数据库或者网络连接的超时控制。
func main(){
	ctx3, cancel3 := context.WithTimeout(context.Background(),time.Millisecond*50) // 设置一个50毫秒的超时
		wg.Add(1)
		go connectDB(ctx3)
		time.Sleep(time.Second * 2)
		cancel3()
		wg.Wait()
}

type TraceCode string
func withvalue(ctxP context.Context) {
	key := TraceCode("TRACE_CODE")
	traceCode, ok := ctxP.Value(key).(string) // 在子goroutine中获取trace code  .()类型断言
	if !ok {
		fmt.Println("invalid trace code")
		return
	}
LOOP:
	for {
		fmt.Printf("worker, trace code:%s\n", traceCode)
		time.Sleep(time.Millisecond * 10) // 假设正常连接数据库耗时10毫秒
		select {
			case <-ctxP.Done(): // 50毫秒后自动调用
				break LOOP
			default:
		}
	}
	wg.Done()
}
//WithValue()
func main(){
	ctxP, cancel := context.WithTimeout(context.Background(), time.Millisecond*50) // 设置一个50毫秒的超时
		// 在系统的入口中设置trace code传递给后续启动的goroutine实现日志数据聚合
		ctxP = context.WithValue(ctxP, TraceCode("TRACE_CODE"), "123123")
		wg.Add(1)
		go withvalue(ctxP)
		time.Sleep(time.Second * 5)
		cancel()
		wg.Wait()
}

你可能感兴趣的:(GO语言,golang)