golang context.Context

antonlin · · 2085 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

# Concurrency In Go # Context 首先提出一个问题,"it is easy to go, but how to stop.",意思就是说,在golang使用go关键字并发非常简单,但是,我们如何去停止一个开启了的go routine。 我们带着这个问题来认识context,首先,我们要明确,正常停止一个go routine的方式只有一种,就是func return.当方法退出了自然go routine就停止了。OK,那我们先看下面代码: ## step.1 go func controlStop(){ //do something //if done. call return return }() 那我们就把停止go rouine转换成为,如何控制func return的问题 ## step.2 var stop =false go func controlStop(stop *bool){ for{ if stop { return }else{ //do something } } }(stop) //another branch control stop = true 我们可以通过上述的模式,即采用自旋加一个全局变量来控制是否返回。是否相比step.1好一些呢。 ## step.3 var stop chan struct{} go func controlStop(stop *chan struct{}){ for{ select { case <-stop: return default://if cancel default,it will be blocked. //do something } } }(&stop) 我们通过一个chan实现了对go routine的控制 ## step.4 如果能够把这种go routine之间的控制,通信封装起来,做为一个STL提供给开发者调用,那么是不是就比较方便,且有统一的接口。OK,context.Context核心概念就是这个,当然,它还有一些升级的机制,但是,本质上,都是在go concurrency中协助go routine的控制。 var log1 *log.Logger func main() { log1 = log.New(os.Stdout, "", log.Ltime) controlStop() } func controlStop() { log1.Println("start") ctx, cancel := context.WithCancel(context.Background()) go doDB1Query(ctx)//启动一个go routine去查询DB1 doDB2Query()//查询DB2 cancel() //cancel db1 time.Sleep(time.Second) } //查询DB2 func doDB2Query() { time.Sleep(time.Second * 3) } //查询DB1 func doDB1Query(ctx context.Context) { log1.Println("do db querying.") for { select { case <-ctx.Done(): log1.Println("ctx done.") return default: time.Sleep(time.Second) log1.Println("db1 waiting db2") } } } 以上就是context的一个最简单的cancel模型。 当然,context还有以下API 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 如,deadline,就会自动超时done,不必等到主动cancel。 --- ## usage 讲了context的作用以及怎么使用,再讲讲应用。 - 网络连接 //1、accept conn //2、start go routine to do something. //3、now conn is closed,can use context to stop all the go routine that associated with the conn. 意思就是:比如一个IOT网络连接,那么我们需要基于这个连接去做一系列的读写操作,其中某些操作还比较费时,那么,我们开启了go routine,有的负责读数据进行持久化,有的负责定时写数据回去(比较耗时),那么,当读的时候发现conn失效,就必须关闭conn,那么我们就可以基于context及时把写数据的go routine给结束掉,而不需要等到出现写错误才自动关闭。 - 请求 比如http请求,使用了context的超时机制,如果读取超时,那么就干嘛干嘛等等。 --- ## impl context的底层实际就是定时器加chan,通过封装这些东西,来实现context的一个控制。 --- ## conclusion go routine的调用是从上而下,是一种树形调用,意味着有root,还有无数的child,因此,context的设计上也符合树形,我们首先基于一个background context创建,而子routine再基于ctx创建更子的ctx,从而实现层次级别的应用上下文关系,这也是为何叫context。 context.WithDeadline(ctx, time.Now()) // Programs that use Contexts should follow these rules to keep interfaces // consistent across packages and enable static analysis tools to check context // propagation: // // Do not store Contexts inside a struct type; instead, pass a Context // explicitly to each function that needs it. The Context should be the first // parameter, typically named ctx: // 不要把Context存在一个结构体当中,显式地传入函数。Context变量需要作为第一个参数使用,一般命名为ctx; // func DoSomething(ctx context.Context, arg Arg) error { // // ... use ctx ... // } // // Do not pass a nil Context, even if a function permits it. Pass context.TODO // if you are unsure about which Context to use. //即使方法允许,也不要传入一个nil的Context,如果你不确定你要用什么Context的时候传一个context.TODO; // Use context Values only for request-scoped data that transits processes and // APIs, not for passing optional parameters to functions. //使用context的Value相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数 // The same Context may be passed to functions running in different goroutines; // Contexts are safe for simultaneous use by multiple goroutines. //同样的Context可以用来传递到不同的goroutine中,Context在多个goroutine中是安全的 在子Context被传递到的goroutine中,应该对该子Context的Done信道(channel)进行监控,一旦该信道被关闭(即上层运行环境撤销了本goroutine的执行),应主动终止对当前请求信息的处理,释放资源并返回。 --- 如果想更加深入了解,可参考官方的信息 https://blog.golang.org/pipelines https://blog.golang.org/context https://www.youtube.com/watch?v=QDDwwePbDtw

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2085 次点击  
加入收藏 微博
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传