go笔记--几个例子理解context的作用


经常在http框架里面看到一个context参数,它是做什么的呢,先简单看看它的定义。

context interface

type Context interface {
// Deadline returns the time when work done on behalf of this context
// should be canceled. Deadline returns ok==false when no deadline is
// set. Successive calls to Deadline return the same results.
Deadline() (deadline time.Time, ok bool) // Done returns a channel that's closed when work done on behalf of this
// context should be canceled. Done may return nil if this context can
// never be canceled. Successive calls to Done return the same value.
//
// WithCancel arranges for Done to be closed when cancel is called;
// WithDeadline arranges for Done to be closed when the deadline
// expires; WithTimeout arranges for Done to be closed when the timeout
// elapses.
//
// Done is provided for use in select statements:
//
// // Stream generates values with DoSomething and sends them to out
// // until DoSomething returns an error or ctx.Done is closed.
// func Stream(ctx context.Context, out chan<- Value) error {
// for {
// v, err := DoSomething(ctx)
// if err != nil {
// return err
// }
// select {
// case <-ctx.Done():
// return ctx.Err()
// case out <- v:
// }
// }
// }
//
// See https://blog.golang.org/pipelines for more examples of how to use
// a Done channel for cancelation.
Done() <-chan struct{} // Err returns a non-nil error value after Done is closed. Err returns
// Canceled if the context was canceled or DeadlineExceeded if the
// context's deadline passed. No other values for Err are defined.
// After Done is closed, successive calls to Err return the same value.
Err() error // Value returns the value associated with this context for key, or nil
// if no value is associated with key. Successive calls to Value with
// the same key returns the same result.
//
// Use context values only for request-scoped data that transits
// processes and API boundaries, not for passing optional parameters to
// functions.
//
// A key identifies a specific value in a Context. Functions that wish
// to store values in Context typically allocate a key in a global
// variable then use that key as the argument to context.WithValue and
// Context.Value. A key can be any type that supports equality;
// packages should define keys as an unexported type to avoid
// collisions.
//
// Packages that define a Context key should provide type-safe accessors
// for the values stored using that key:
//
// // Package user defines a User type that's stored in Contexts.
// package user
//
// import "context"
//
// // User is the type of value stored in the Contexts.
// type User struct {...}
//
// // key is an unexported type for keys defined in this package.
// // This prevents collisions with keys defined in other packages.
// type key int
//
// // userKey is the key for user.User values in Contexts. It is
// // unexported; clients use user.NewContext and user.FromContext
// // instead of using this key directly.
// var userKey key = 0
//
// // NewContext returns a new Context that carries value u.
// func NewContext(ctx context.Context, u *User) context.Context {
// return context.WithValue(ctx, userKey, u)
// }
//
// // FromContext returns the User value stored in ctx, if any.
// func FromContext(ctx context.Context) (*User, bool) {
// u, ok := ctx.Value(userKey).(*User)
// return u, ok
// }
Value(key interface{}) interface{}
}

可以看到它一个接口类型、主要包含4个成员,我们暂时不知道它们的意义。

    Deadline() (deadline time.Time, ok bool)

    Done() <-chan struct{}

    Err() error

    Value(key interface{}) interface{}

先看一个简单的例程

package main

import (
"context"
"fmt"
"time"
) func cancel1() {
// 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():
fmt.Println("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
}
}
} func main() { fmt.Println("-----start-----") fmt.Println("call cancel1()")
go cancel1()
fmt.Println("cancel1() end") time.Sleep(time.Second)
fmt.Println("----end------")
}

控制台会输出

我们可以看到,for循环执行了5次 context 的派生函数gen()后,通过cancel()函数退出了gen里面的协程。

那么它在这里的作用也就清楚了:

context的作用

设置截止日期,超时或调用取消函数来通知所有使用任何派生 context 的函数来停止运行并返回。

咱们这是取消函数

contxt相关函数

withcancel

此函数创建从传入的父 context 派生的新 context。父 context 可以是后台 context 或传递给函数的 context。

返回派生 context 和取消函数。只有创建它的函数才能调用取消函数来取消此 context。

deadline

WithDeadline()返回其父项的派生 context,当截止日期超过或取消函数被调用时,该 context 将被取消。例如,您可以创建一个将在以后的某个时间自动取消的 context,并在子函数中传递它。当因为截止日期耗尽而取消该 context 时,获此 context 的所有函数都会收到通知去停止运行并返回。

小例:

func deadline1() {
d := time.Now().Add(1200 * 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())
} } func main() { fmt.Println("-----start-----") fmt.Println("call deadline1()")
go deadline1()
fmt.Println("deadline1() end") time.Sleep(3*time.Second)
fmt.Println("----end------")
}

withtimeout()和WithDeadline()类似,不同之处在于它将持续时间作为参数输入而不是时间对象。

timeout

// Pass a context with a timeout to tell a blocking function that it
// should abandon its work after the timeout elapses.
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"
} Output:
context deadline exceeded

现在再看看这context的interface,就能简单明了它们的意义了。

	// 返回超时期限。
Deadline() (deadline time.Time, ok bool) // 辅助cancel
Done() <-chan struct{} // 获取返回的错误
Err() error Value(key interface{}) interface{}

可是还有个Value, 那我们再看看。

还有一个withValue() 函数 、

此函数接收 context 并返回派生 context,其中值 val 与 key 关联,并通过 context 树与 context 一起传递。这意味着一旦获得带有值的 context,从中派生的任何 context 都会获得此值。不建议使用 context 值传递关键参数,而是函数应接收签名中的那些值,使其显式化。

Code:
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")) Output:
found value: Go
key not found: color

那么明显了 Value(key interface{}) interface{} 返回的即是context关联的键值。

go笔记--几个例子理解context的作用的更多相关文章

  1. Android深入理解Context(二)Activity和Service的Context创建过程

    前言 上一篇文章我们学习了Context关联类和Application Context的创建过程,这一篇我们接着来学习Activity和Service的Context创建过程.需要注意的是,本篇的知识 ...

  2. 全面理解Context

    出处:http://blog.csdn.net/lmj623565791/article/details/40481055,本文出自:[张鸿洋的博客] 本文大多数内容翻译自:http://www.do ...

  3. Android-5 理解context

    context -- 用来访问全局信息 Application用途 Application生命周期 深入理解 Context http://blog.csdn.net/z1074971432/arti ...

  4. 通过四个例子理解JavaScript拓展运算符

    原文地址:JavaScript & The spread operator 拓展运算符看起来像什么? 三个点,... 它能做什么? 拓展运算符允许一个表达式在某个地方展开成为多个元素.变量或参 ...

  5. Mysql加锁过程详解(6)-数据库隔离级别(2)-通过例子理解事务的4种隔离级别

    Mysql加锁过程详解(1)-基本知识 Mysql加锁过程详解(2)-关于mysql 幻读理解 Mysql加锁过程详解(3)-关于mysql 幻读理解 Mysql加锁过程详解(4)-select fo ...

  6. 最简单的例子理解Javascript闭包

    理解Javascript的闭包非常关键,本篇试图用最简单的例子理解此概念. function greet(sth){ return function(name){ console.log(sth + ...

  7. 【转载】MDX Step by Step 读书笔记(三) - Understanding Tuples (理解元组)

    1. 在 Analysis Service 分析服务中,Cube (多维数据集) 是以一个多维数据空间来呈现的.在Cube 中,每一个纬度的属性层次结构都形成了一个轴.沿着这个轴,在属性层次结构上的每 ...

  8. Android中Context的理解及使用(一)——Context的作用

    Context的作用:用来访问全局信息的接口,通过Context进行资源的访问. 1.Context获取字符串资源: public class MainActivity extends AppComp ...

  9. Android源码分析-全面理解Context

    前言 Context在android中的作用不言而喻,当我们访问当前应用的资源,启动一个新的activity的时候都需要提供Context,而这个Context到底是什么呢,这个问题好像很好回答又好像 ...

随机推荐

  1. Codeforces Round #617 (Div. 3)F. Berland Beauty

    题意: 给一棵树,边权未知,现在给m组约束,每组约束给出从u到v路径中的最小值,现在让你给出一组边权,使得符合之前的约束,不能给出输出-1 思路: 因为n较小,对于每组约束我们可以直接暴力修改路径上的 ...

  2. 优雅对API进行内部升级改造

    优雅对API进行内部升级改造 背景 随着业务的快速发展老的系统将逐渐的无法快速支撑现有业务迭代重构一个必然的过程;然而在底层业务系统重构的过程中,对外提供的API也同时需要进行相应的升级替换;推动外部 ...

  3. 初识Redis,看这一篇就够了

    环境的搭建和安装网上有很多教程,在这里就不再重复了. 1. Redis是什么? Redis(全称:Remote Dictionary Server 远程字典服务)是一个开源的使用ANSI C语言编写. ...

  4. 死磕mysql(6)

    再写数据库作业的时候,发现了一个问题,如果存在主键外键的约束,数据就删不掉 --set foreign_key_checks=0; 关掉外键约束 用好了再打开 --set foreign_key_ch ...

  5. Linux磁盘管理及挂载

    1.在vm workstations添加磁盘 添加完之后重启一下虚拟机 然后fdisk -l 可以看到虚拟机已经识别到了新的磁盘 2.添加磁盘分区 输入命令 fdisk /dev/sdb 命令(输入 ...

  6. RT600 I2S外设介绍及应用

    恩智浦的i.MX RT600是跨界处理器产品,同样也是i.MX RTxxx系列的开山之作.不同于i.MX RT1xxx系列单片机,i.MX RT600 采用了双核架构,将新一代Cortex-M33内核 ...

  7. Web框架之Gin介绍及使用

    Gin是一个用Go语言编写的web框架.它是一个类似于martini但拥有更好性能的API框架, 由于使用了httprouter,速度提高了近40倍. 如果你是性能和高效的追求者, 你会爱上Gin. ...

  8. HDU_2579_bfs

    http://acm.split.hdu.edu.cn/showproblem.php?pid=2579 简单bfs题,刚开始在纠结怎么存放vis,因为步数可能有几百步,这么多格子开数组的话也太多了, ...

  9. Luogu P1280 尼可的任务(dp)

    题意: 时间为n,有k个任务,每个任务有一个开始时间和持续时间,从第一分钟开始,如果有开始的任务就要做,问最大空闲时间 n,k<=1e5 思路: 设 dp[i]为i~n时间中最大空闲时间,vec ...

  10. 从底层入手,解析字节码增强和Btrace应用

    这篇文章聊下字节码和相关的应用. 1.机器码和字节码 机器码(machine code),学名机器语言指令,有时也被称为原生码(Native Code),是电脑的CPU可直接解读的数据. 通常意义上来 ...