• Go并发控制--context的使用


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

  • 相关阅读:
    Redis&PHP的使用安装-windows版
    【JAVA】使用Eclipse依赖生成jar包时,避免最外层同时生成资源文件的配置。
    【ActiveMQ】重写监听
    【ActiveMQ】设置自动重连
    【ActiveMQ】持久化消息队列的三种方式
    【Spring】手动获取spring容器对象时,报no qualifying bean of type is defined
    【监控】使用probe对tomcat服务进行监控
    【前端】使用weinre对手机、微信浏览器页面调试
    【前端】一句命令快速合并压缩 JS、CSS
    【前端】CSS雪碧
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/9197753.html
Copyright © 2020-2023  润新知