我们已经知道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. SpringBoot(二)thymeleaf模板的引入

    接着上一次的配置 1.在pom文件中添加thymeleaf模板的引入, <dependency> <groupId>org.springframework.boot</g ...

  2. Access denied for user 'root'@'IP' (using password:YES)解决方法

    在MySql的使用过程中,碰到“Access denied for user 'root'@'IP' (using password:YES)”的问题,使用以下语句修改后还是不行. GRANT ALL ...

  3. prototype和_proto_

    __proto__(隐式原型)与prototype(显式原型) 显式原型 explicit prototype property:用来实现基于原型的继承与属性的共享. 每一个函数在创建之后都会拥有一个 ...

  4. C++指针易错点梳理

    1 指针定义 指针是一个变量:指针的值是另一个变量的地址.变量的声明 type *var-name; var-name 是指针变量的名称.星号是用来指定一个变量var-name是指针变量. int * ...

  5. 深入理解Java中的多态

    一.什么是多态? 多态指同一个实体同时具有多种形式.它是面向对象程序设计(OOP)的一个重要特征.如果一个语言只支持类而不支持多态,只能说明它是基于对象的,而不是面向对象的. 二.多态是如何实现的? ...

  6. WEBBASE篇: 第二篇, HTML知识2

    HTML知识2 <!doctype html> <html lang="en"> <head> <meta charset="U ...

  7. Implementing a CNN for Text Classification in TensorFlow

    参考: 1.Understanding Convolutional Neural Networks for NLP 2.Implementing a CNN for Text Classificati ...

  8. AE旋转

    精准对位: 好几个图层上的旋转点在一个位置上: 方法1:勾选网格,定点. 方法2:按住ctrl+r  调出尺寸.拖参考线,焦点自动吸附功能. 选中四张或者选中第一张,按shift键,选中最后一张(即可 ...

  9. markdown使用问题

    1.配置自定义的markdown.css https://github.com/sameer1994kiki/markdown-css 2.代码块 一行`` 多行 ``` <code>&l ...

  10. react写单选按钮或table标签

    首先,原理是一样的: class Loca_choose_wrap extends Component{ constructor(){ super(); this.state={ port_name: ...