Context 通常被译作 上下文 ,一般理解为程序单元的一个运行状态、现场、快照,而翻译中 上下 又很好地诠释了其本质,上下上下则是存在上下层的传递,  会把内容传递给  。

在Go语言中,程序单元也就指的是Goroutine。每个Goroutine在执行之前,都要先知道程序当前的执行状态,通常将这些执行状态封装在一个Context 变量中,传递给要执行的Goroutine中。上下文则几乎已经成为传递与请求同生存周期变量的标准方法。

context 包不仅实现了在程序单元之间共享状态变量的方法,同时能通过简单的方法,使我们在被调用程序单元的外部,通过设置ctx变量值,将过期或撤销这些信号传递给被调用的程序单元。在网络编程中,若存在A调用B的API, B再调用C的API,若A调用B取消,那也要取消B调用C,通过在A,B,C的API调用之间传递 Context ,以及判断其状态,就能解决此问题,这是为什么gRPC的接口中带上 ctx context.Context 参数的原因之一。

context 包的核心就是 Context 接口,其定义如下:

type Context interface {
Deadline() (deadline time.Time, ok bool)
Done() <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
  • Deadline 会返回一个超时时间,Goroutine获得了超时时间后,例如可以对某些io操作设定超时时间。

  • Done 方法返回一个信道(channel),当 Context 被撤销或过期时,该信道是关闭的,即它是一个表示Context是否已关闭的信号。

  • 当 Done 信道关闭后, Err 方法表明 Contex t被撤的原因。

  • Value 可以让Goroutine共享一些数据,当然获得数据是协程安全的。但使用这些数据的时候要注意同步,比如返回了一个map,而这个map的读写则要加锁。

Context 接口没有提供方法来设置其值和过期时间,也没有提供方法直接将其自身撤销。也就是说, Context 不能改变和撤销其自身。那么该怎么通过 Context 传递改变后的状态呢?

context使用

无论是Goroutine,他们的创建和调用关系总是像层层调用进行的,就像人的辈分一样,而更靠顶部的Goroutine应有办法主动关闭其下属的Goroutine的执行(不然程序可能就失控了)。为了实现这种关系,Context结构也应该像一棵树,叶子节点须总是由根节点衍生出来的。

要创建Context树,第一步就是要得到根节点, context.Background 函数的返回值就是根节点:

func Background() Context

该函数返回空的Context,该Context一般由接收请求的第一个Goroutine创建,是与进入请求对应的Context根节点,它不能被取消、没有值、也没有过期时间。它常常作为处理Request的顶层context存在。

有了根节点,又该怎么创建其它的子节点,孙节点呢?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 interface{}, val interface{}) Context

函数都接收一个 Context 类型的参数 parent ,并返回一个 Context 类型的值,这样就层层创建出不同的节点。子节点是从复制父节点得到的,并且根据接收参数设定子节点的一些状态值,接着就可以将子节点传递给下层的Goroutine了。

再回到之前的问题:该怎么通过 Context 传递改变后的状态呢?使用 Context 的Goroutine无法取消某个操作,其实这也是符合常理的,因为这些Goroutine是被某个父Goroutine创建的,而理应只有父Goroutine可以取消操作。在父Goroutine中可以通过WithCancel方法获得一个cancel方法,从而获得cancel的权利。

第一个 WithCancel 函数,它是将父节点复制到子节点,并且还返回一个额外的 CancelFunc 函数类型变量,该函数类型的定义为:

type CancelFunc func()

调用 CancelFunc 对象将撤销对应的 Context 对象,这就是主动撤销 Context 的方法。在父节点的 Context 所对应的环境中,通过 WithCancel 函数不仅可创建子节点的 Context ,同时也获得了该节点 Context 的控制权,一旦执行该函数,则该节点 Context 就结束了,则子节点需要类似如下代码来判断是否已结束,并退出该Goroutine:

select {    case <-cxt.Done():
// do some clean...
}

WithDeadline 函数的作用也差不多,它返回的Context类型值同样是 parent 的副本,但其过期时间由 deadline 和 parent 的过期时间共同决定。当 parent 的过期时间早于传入的 deadline 时间时,返回的过期时间应与 parent 相同。父节点过期时,其所有的子孙节点必须同时关闭;反之,返回的父节点的过期时间则为 deadline 。

WithTimeout 函数与 WithDeadline 类似,只不过它传入的是从现在开始Context剩余的生命时长。他们都同样也都返回了所创建的子Context的控制权,一个 CancelFunc 类型的函数变量。

当顶层的Request请求函数结束后,我们就可以cancel掉某个context,从而层层Goroutine根据判断 cxt.Done() 来结束。

WithValue 函数,它返回 parent 的一个副本,调用该副本的Value(key)方法将得到val。这样我们不光将根节点原有的值保留了,还在子孙节点中加入了新的值,注意若存在Key相同,则会被覆盖。

范例:

package main

import (
"fmt"
"time"
"golang.org/x/net/context"
) func main() {
// ctx, cancelFunc := context.WithDeadline(context.Background(), time.Now().Add(time.Second*5))
ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*5)
ctx = context.WithValue(ctx, "Test", "123456")
// defer cancelFunc() if t, ok := ctx.Deadline(); ok {
fmt.Println(time.Now())
fmt.Println(t.String())
}
go func(ctx context.Context) {
fmt.Println(ctx.Value("Test"))
for {
select {
case <-ctx.Done():
fmt.Println(ctx.Err())
return
// default:
// continue
}
}
}(ctx)
// if ctx.Err() == nil {
// fmt.Println("Sleep 10 seconds...")
// time.Sleep(time.Second * 10)
// }
// if ctx.Err() != nil {
// fmt.Println("Alredy exit...")
// }
time.Sleep(time.Second * 3)
cancelFunc()
// for {
// if ctx.Err() != nil {
// fmt.Println("gracefully exit...")
// break
// }
// }
}

参考:http://lanlingzi.cn/post/technical/2016/0802_go_context/?utm_source=tuicool&utm_medium=referral

http://blog.csdn.net/u014029783/article/details/53782864

http://blog.csdn.net/zdyueguanyun/article/details/64904703

Golang context包解读的更多相关文章

  1. Golang Context 包详解

    Golang Context 包详解 0. 引言 在 Go 语言编写的服务器程序中,服务器通常要为每个 HTTP 请求创建一个 goroutine 以并发地处理业务.同时,这个 goroutine 也 ...

  2. golang context包

    go context标准库 context包在Go1.7版本时加入到标准库中.其设计目标是给Golang提供一个标准接口来给其他任务发送取消信号和传递数据.其具体作用为: 可以通过context发送取 ...

  3. 【GoLang】golang context channel 详解

    代码示例: package main import ( "fmt" "time" "golang.org/x/net/context" ) ...

  4. Golang Context 的原理与实战

    本文让我们一起来学习 golang Context 的使用和标准库中的Context的实现. golang context 包 一开始只是 Google 内部使用的一个 Golang 包,在 Gola ...

  5. golang中的context包

    标准库的context包 从设计角度上来讲, golang的context包提供了一种父routine对子routine的管理功能. 我的这种理解虽然和网上各种文章中讲的不太一样, 但我认为基本上还是 ...

  6. golang中context包学习

    摘要 go语言中goroutine之间的关联关系,缺乏维护,在erlang中有专门的机制来保障新开仟程的生命周期, 在go语言中,只能通过channel + select来实现,但不够直观,感觉很绕. ...

  7. Golang Context 详细介绍

    Golang context 本文包含对context实现上的分析和使用方式,分析部分源码讲解比价多,可能会比较枯燥,读者可以直接跳过去阅读使用部分. ps: 作者本着开源分享的精神撰写本篇文章,如果 ...

  8. golang context 剖析 1.7.4 版本

    1. 内部结构之 - timerCtx . type timerCtx struct { cancelCtx timer *time.Timer // Under cancelCtx.mu. dead ...

  9. 简析 Golang IO 包

    简析 Golang IO 包 io 包提供了 I/O 原语(primitives)的基本接口.io 包中定义了四个最基本接口 Reader.Writer.Closer.Seeker 用于表示二进制流的 ...

随机推荐

  1. 南阳nyoj 509 因子和阶乘

    因子和阶乘 时间限制:1000 ms  |  内存限制:65535 KB 难度:2  http://acm.nyist.net/JudgeOnline/problem.php?pid=509 描述 给 ...

  2. JDBC程序优化--提取配置信息放到属性文件中

    JDBC程序优化--提取配置信息放到属性文件中 此处仅仅优化JDBC连接部分,代码如下: public class ConnectionFactory { private static String ...

  3. 深入理解MyBatis的原理(三):配置文件(上)

    前言:前文提到一个入门的demo,从这里开始,会了解深入 MyBatis 的配置,本文讲解 MyBatis 的配置文件的用法. 目录 1.properties 元素 2.设置(settings) 3. ...

  4. Golang beego ORM + CRUP 操作详解

      构建beego Web 项目         首先构建一个beego 的web 项目,这个我们完全可以参考beego 官网中的开发文档,上面介绍的非常的详细,在这我就不给大家介绍,主要是介绍ORM ...

  5. 慕课网 Ajax笔记

    Ajax技术实现: 运用HTML和CSS来实现页面,表达信息: 运用XMLHttpRequest和web服务器进行数据的异步交换: 运用JavaScript操作DOM,实现动态局部刷新: 同步:就是用 ...

  6. LNMP下安装memcache

    转自:LNMP 添加 memcached服务 由于memcached具有更多的功能和服务,已经不推荐使用memcache了.(缺少个字母d) 1. 首先安装memcached服务端. 这里使用yum源 ...

  7. drupal7 addExpression+union+分页

    global $user; $query_single = db_select('mp_order_singlepay', 'ms') ->fields('ms', array('order_i ...

  8. c# 异步和同步问题(转载)

    [C#] 谈谈异步编程async await   为什么需要异步,异步对可能起阻止作用的活动(例如,应用程序访问 Web 时)至关重要. 对 Web 资源的访问有时很慢或会延迟. 如果此类活动在同步过 ...

  9. request对象方法

    1.html <html> <head> <meta http-equiv="Content-Type" content="text/htm ...

  10. Python 中单双引号

    TODO, 在python中, 其实单双引号还是有分别的, 具体是什么?