我们已经知道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. apache .htacess

    htaccess 详解   .htaccess是什么 .htaccess文件(或者"分布式配置文件")提供了针对目录改变配置的方法, 即,在一个特定的文档目录中放置一个包含一个或多 ...

  2. django面试题必问

    1.谈谈你对http协议的认识. HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议.它可以使浏览器更加高效,使 ...

  3. pycharm 永久解封

    第一步   c:\windows\system32\drivers\etc    命令行输入这个 第二步     把host文件复制到桌面 第三步   记事本打开host 第四步    在最下面添加  ...

  4. jenkins + nodejs + git 自动化部署前端

    1.  创建自定义风格任务 2.填写项目描述 3.配置源码管理 4. 系统管理->插件管理 ->安装插件 5.配置系统管理->全局工具配置-> 6.配置全局 ssh 7. 继续 ...

  5. 16 多校8 Rikka with Parenthesis II

    As we know, Rikka is poor at math. Yuta is worrying about this situation, so he gives Rikka some mat ...

  6. 【Python】etree方法生成,解析xml

    #练习:另一种遍历xml文件的方式etree,xpathimport systry: import xml.etree.cElementTree as ET #前面带c的都是比较快的,效率高且不占内存 ...

  7. [转]TDD之Dummy Stub Fake Mock

    TDD之Dummy Stub Fake Mock 测试驱动大家都很熟悉了,这两天正好看了一个java的书,对TDD中的一些基本概念进行了复习,具体如下: Dummy An object that is ...

  8. 2.13 table表格定位

    2.13 table表格定位 前言    在web页面中经常会遇到table表格,特别是后台操作页面比较常见.本篇详细讲解table表格如何定位.一.认识table    1.首先看下table长什么 ...

  9. HDU 2561

    F - 第二第二 Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Prac ...

  10. (1)什么是web框架和http协议

    Django是一个web框架 web框架的本质:就是一个socket服务端 bs架构本质上就是cs架构(cs架构就是client和server):bs架构就是browser和server,本质上bro ...