我们已经知道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. python里面如何拷贝一个对象,deecopy和copy的区别

    copy仅拷贝对象的本身,而不拷贝对象引用的其它对象. deecopy除拷贝对象本身,而拷贝对象引用的其它对象.

  2. python 爬虫数据时间转换格式

    from datetime import datetimea = '2018/9/18 10/10'print(datetime.strptime(a,'%Y/%m/%d %H/%M'))>&g ...

  3. tp5 生成缩略图片

    我先说下我的思路,先把正常图片存到服务器,再通过代码将服务器上的大图压缩,生成新的小图替代大图 下面上代码 前台HTML代码 <div class="upload-btn"& ...

  4. PAT 乙级 1056. 组合数的和(15)

    给定N个非0的个位数字,用其中任意2个数字都可以组合成1个2位的数字.要求所有可能组合出来的2位数字的和.例如给定2.5.8,则可以组合出:25.28.52.58.82.85,它们的和为330. 输入 ...

  5. git clone新项目后如何拉取分支代码到本地

    1.git clone git@git.n.xxx.com:xxx/xxx.git 2.git fetch origin dev    命令来把远程dev分支拉到本地 3.checkout -b de ...

  6. ES6 class的基本语法-学习笔记

    1.基本上,ES6 的class可以看作只是一个语法糖,它的绝大部分功能,ES5 都可以做到,新的class写法只是让对象原型的写法更加清晰.更像面向对象编程的语法而已. 类的内部所有定义的方法,都是 ...

  7. make命令回显Makefile执行脚本命令

    /********************************************************************** * make命令回显Makefile执行脚本命令 * 说 ...

  8. jqGrid 清空单元格的方法

    在工作中,使用到jqGrid的场景是比较多的. 今天在工作中遇到了一个jqGrid的问题,当将单元格的值设置成 '' 空字符串的时候,竟然不会修改成功. $("#mygrid"). ...

  9. day 019 常用模块

    主要内容: 1模块的简单认识 2collection模块 3time时间模块 4random模块 5os模块 6sys模块 一  模块的简单认识 引入模块的方式:  1import (常见方式) 2 ...

  10. 【BZOJ2154】Crash的数字表格

    算是学会反演了……(其实挺好学的一天就能学会…… 原题: 今天的数学课上,Crash小朋友学习了最小公倍数(Least Common Multiple).对于两个正整数a和b,LCM(a, b)表示能 ...