我们已经知道WaitGroup可以用于并发控制,但当遇到更复杂的场景时,例如主动取消goroutine或者使超时的goroutine自动退出等,WaitGroup就无能为力。

这个时候,就是context大有用武之地。

context定义了Context类型,它跨API边界和进程之间携带截止日期,取消信号和其他请求范围的值。

对服务器的传入请求应创建一个Context,对服务器的传出调用应接受一个Context。它们之间的函数调用链必须传播Context,或者传递使用WithCancel,WithDeadline,WithTimeout或WithValue创建的派生Context。当取消一个context后,所有从这个context派生的context也会被取消。

WithCancel,WithDeadline和WithTimeout函数接受Context(父)并返回派生的Context(子)和CancelFunc。调用CancelFunc会取消子项及其子项,删除父项对子项的引用,并停止任何关联的计时器。未能调用CancelFunc会泄漏子项及其子项,直到取消父项或计时器触发。 go vet工具检查CancelFuncs是否在所有控制流路径上使用。

使用Contexts的程序应遵循这些规则,以使各包之间的接口保持一致,并启用静态分析工具来检查上下文传播:

  • 不要将Contexts存储在结构类型中;相反,将Context明确传递给需要它的每个函数。Context应该是第一个参数,通常命名为ctx:func DoSomething(ctx context.Context, arg Arg) error { // ... use ctx ... }
  • 即使函数允许,也不要传递nil Context。如果您不确定要使用哪个Context,请传递context.TODO。
  • 仅将context values用于进程间和跨API的请求范围数据,而不是将其作为可选参数传递给函数。
  • 可以将相同的Context传递给在不同goroutine中运行的函数;上下文对于多个goroutine同时使用是安全的。

以上说明来自context包说明,如果感觉翻译的不够清楚,可以查看context的说明。(实在翻译不下去了 = =!)

Context定义如下:

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

说了这么多,现在来看下例子吧。

并发控制 Cancel Example

通过使用WithCancel可以主动取消一个或多个goroutine的执行,以实现对并发的控制。

package main 

import (
"context"
"fmt"
"time"
) func PrintTask(ctx context.Context) { for { select { case <- ctx.Done():
return
default:
time.Sleep(2*time.Second)
fmt.Println("A man must walk down many roads.")
} } } func main() { ctx := context.Background()
ctx, cancel := context.WithCancel(ctx) defer cancel() go PrintTask(ctx)
go PrintTask(ctx)
go PrintTask(ctx) time.Sleep(3*time.Second)
fmt.Println("main exit...")
}

WithCancel函数原型如下

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

WithCancel返回两个结果,一个是context(parent的副本,并带有新的Done channel), 一个是cancel函数。

当调用cancel函数或者parent的Done channel关闭时,新返回的Done channel就会被关闭。

例子中,调用cancel函数,关闭了Done channel后,进而PrintTask就会结束。

output

A man must walk down many roads.

A man must walk down many roads.

A man must walk down many roads.

main exit...

并发超时控制 Timeout Example

WithTimeout可以实现并发超时控制,使goroutine执行超时时自动结束。

package main 

import (
"context"
"fmt"
"time"
) func main() { ctx := context.Background()
timeout := 50*time.Millisecond
ctx, cancel := context.WithTimeout(ctx, timeout) defer cancel() done := make(chan int,1) go func() {
// do something
time.Sleep(1*time.Second)
done<- 1
}() select{
case <-done:
fmt.Println("work done on time")
case <-ctx.Done():
// timeout
fmt.Println(ctx.Err())
} fmt.Println("main exit...")
}

例子中,使用WithTimeout()函数,其实现上,直接调用

WithDeadline(parent, time.Now().Add(timeout))

WithDeadline函数定义如下:

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

传入参数:

  • Context类型的变量parent
  • timeout超时时间

返回两个结果:

  • Context类型的变量
  • 取消函数cancel

当出现超时,或者调用取消函数cancel,或者parentDonechannel被关闭时,

新返回的context的Done channel将被关闭。

output:

context deadline exceeded

main exit...

更多参考

golang context学习记录1

golang context学习记录2

Go并发控制--context的使用的更多相关文章

  1. 并发控制--context篇

    目录 1. 前言 2 Context 实现原理 2.1 接口定义 2.1 cancelCtx 2.1.1 Done()接口实现 2.1.2 Err()接口实现 2.1.3 cancel()接口实现 2 ...

  2. golang context学习记录1

    1.前言 一个请求,可能涉及多个API调用,多个goroutine,如何在多个API 之间,以及多个goroutine之间协作和传递信息,就是一个问题. 比如一个网络请求Request,需要开启一些g ...

  3. Go并发控制--WaitGroup篇

    目录 1. 前言 2. 使用WaitGroup控制 2.1 使用场景 2.2 信号量 1.3 WaitGroup 数据结构 2.3.1 Add () 方法 2.3.2 Wait() 2.3.3 Don ...

  4. EntityFramework与TransactionScope事务和并发控制

    最近在园子里看到一篇关于TransactionScope的文章,发现事务和并发控制是新接触Entity Framework和Transaction Scope的园友们不易理解的问题,遂组织此文跟大家共 ...

  5. 数据访问模式:数据并发控制(Data Concurrency Control)

    1.数据并发控制(Data Concurrency Control)简介 数据并发控制(Data Concurrency Control)是用来处理在同一时刻对被持久化的业务对象进行多次修改的系统.当 ...

  6. Entity Framework 乐观并发控制

    一.背景 我们知道,为了防止并发而出现脏读脏写的情况,可以使用Lock语句关键字,这属于悲观并发控制的一种技术,,但在分布式站点下,锁的作用几乎不存在,因为虽然锁住了A服务器的实例对象,但B服务器上的 ...

  7. Kubernetes并发控制与数据一致性的实现原理

    在大型分布式系统中,定会存在大量并发写入的场景.在这种场景下如何进行更好的并发控制,即在多个任务同时存取数据时保证数据的一致性,成为分布式系统必须解决的问题.悲观并发控制和乐观并发控制是并发控制中采用 ...

  8. EF6 Code First 系列 (四):SQLite的DropCreateDatabaseIfModelChanges和乐观并发控制

    没什么好说的,能支持DropCreateDatabaseIfModelChanges和RowVersion的Sqlite谁都想要.EntityFramework7正在添加对Sqlite的支持,虽然EF ...

  9. 深度解密Go语言之context

    目录 什么是 context 为什么有 context context 底层实现原理 整体概览 接口 Context canceler 结构体 emptyCtx cancelCtx timerCtx ...

随机推荐

  1. react 学习笔记 npm 命令

    第一步: cnpm install --save react react-dom babelify babel-preset-react 第二步: 安装es2015 cnpm install babe ...

  2. REST是什么?

    REST -- REpresentational State Transfer 直接翻译:表现层状态转移.   @Ivony 老师的一句话概括很精辟: 用URL定位资源,用HTTP动词(GET,POS ...

  3. Spring Boot 揭秘与实战(二) 数据存储篇 - MySQL

    文章目录 1. 环境依赖 2. 数据源3. 脚本初始化 2.1. 方案一 使用 Spring Boot 默认配置 2.2. 方案二 手动创建 4. 使用JdbcTemplate操作5. 总结 4.1. ...

  4. Can't locate find.pl in @INC (@INC contains: /etc/perl xxxx) at perlpath.pl line 7.

    /********************************************************************** * Can't locate find.pl in @I ...

  5. flask中的request和response

    1.request 1.request的常用成员 1.scheme : 获取请求方案(协议) 2.method : 获取本期请求的请求方式(重点) 3.args : 获取使用get请求方式提交的数据 ...

  6. websocket介绍 以及 vue websocket使用案例

    1 介绍: https://www.zhihu.com/question/20215561 2 案例: https://www.jianshu.com/p/0d20a032d0ec (坑说明). ht ...

  7. day 017面向对象-反射

    主要内容: isinstance, type, issubclass( 内置函数) 区分函数和方法 反射 一    ( isinstance, type, issubclass) isinstance ...

  8. 如何解决VMware 12 安装Ubuntu 16.04时无网络连接问题

    刚安装玩Ubuntu,打开后上网没有网络连接 ,点击右上角的数据连接,显示已经启动联网,但是用火狐还是无法上网: 解决方法如下: 先查看虚拟机的网络适配器:点击虚拟机左上角的编辑,里面有个网络适配器 ...

  9. 神坑,使用gcc对opencv重编译

    工具 cmake-3.9.1 opencv-3.3.0  gcc-6.3.0 前几天为了装eclipse的CDT,把minggw里的make改了,今天突然想学opencv,可是只找到了vc版本,千辛万 ...

  10. hdu3338 Kakuro Extension 最大流

    If you solved problem like this, forget it.Because you need to use a completely different algorithm ...