• golang context学习记录1


    1.前言

    一个请求,可能涉及多个API调用,多个goroutine,如何在多个API 之间,以及多个goroutine之间协作和传递信息,就是一个问题。

    比如一个网络请求Request,需要开启一些goroutine去访问后端资源(比如,数据库,RPC服务等),这些goroutine又可能会开启其他的goroutine,如何跟踪和控制这些goroutine呢?

    golang定义了 context包,用于解决这个问题。

    context可以在多个API和进程之间,实现deadlines截止日期操作、cancelation signals取消操作以及传递request-scoped的值(即传递参数)。

    context一个最大的特点是可以继承。父context,可以派生子context,子context有又可以派生子context。如果父context被取消,子context及其子context都会被取消。

    常用派生的方法有:WithCancel WithDeadline WithTimeout WithValue.
    其中,

    • WithCancel用于主动取消自身以及子context。
    • WithDeadlineWithTimeout用于超时情况下的取消自身以及子context。WithTimeout实现上直接调用WithDeadline
    • WithValue用于context传递request-scoped的参数,而不是向函数传递的可选参数。

    2.例子

    下面例子演示如何控制多个goroutine的退出。

    首先,启动3个goroutine,分别命名为"1"、"2"、"3",每个goroutine又启动一个goroutine。也就是有6个goroutine。它们之间的关系如下:

    • 1
      • 11
    • 2
      • 21
    • 3
      • 31

    最后,在main中调用取消操作cancel,先是1,2,3收到信号退出,1,2,3退出的时候又调用它们的cancel,这样所有的goroutine都被取消并退出。

    具体代码如下:

    package main
    
    import (
            "context"
            "fmt"
            "time"
    )
    
    
    func PrintTask(ctx context.Context, taskName string) {
    
            for {
    
                    select {
    
                    case <- ctx.Done():
                            fmt.Println("task:", taskName, " exit...")
                            return
                    default:
                            time.Sleep(1*time.Second)
                            fmt.Println("task:", taskName, " doing something...")
                    }
    
            }
    
    }
    
    func mainTask(ctx context.Context, taskName string) {
    
            ctx1, cancel := context.WithCancel(ctx)
            defer cancel()
    
            // create a new task
            newTaskName := taskName + "1"
            go PrintTask(ctx1, newTaskName)
    
    
            for {
    
                    select {
    
                    case <- ctx.Done():
                            fmt.Println("task:", taskName, " exit...")
                            return
                    default:
                            time.Sleep(1*time.Second)
                            fmt.Println("task:", taskName, " doing something...")
                    }
    
            }
    
    
    }
    
    func main() {
    
            ctx := context.Background()
            ctx, cancel := context.WithCancel(ctx)
    
            go mainTask(ctx, "1")
            go mainTask(ctx, "2")
            go mainTask(ctx, "3")
    
            time.Sleep(3*time.Second)
            cancel()
            fmt.Println("main exit...")
            time.Sleep(3*time.Second)
    }
    

    输出

    task: 1  doing something...
    task: 21  doing something...
    task: 11  doing something...
    task: 3  doing something...
    task: 2  doing something...
    task: 31  doing something...
    task: 3  doing something...
    task: 2  doing something...
    task: 11  doing something...
    task: 21  doing something...
    task: 31  doing something...
    task: 1  doing something...
    task: 11  doing something...
    task: 3  doing something...
    task: 1  doing something...
    task: 21  doing something...
    task: 2  doing something...
    task: 31  doing something...
    main exit...
    task: 11  doing something...
    task: 11  exit...
    task: 21  doing something...
    task: 21  exit...
    task: 3  doing something...
    task: 3  exit...
    task: 31  doing something...
    task: 31  exit...
    task: 1  doing something...
    task: 1  exit...
    task: 2  doing something...
    task: 2  exit...
    

    更多参考

    Golang并发控制--context的使用

    Golang Context深入理解

  • 相关阅读:
    Openlayer 3 的画图测量面积
    Openlayer 3 的画线测量长度
    屏幕尺寸
    px和em,rem的区别
    水平和垂直居中
    Flex布局
    继承的几种方法及优缺点
    call ,apply 和 bind的用法与区别
    mybatis springmvc velocity的demo
    正则同时包含两个关键字
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/10890241.html
Copyright © 2020-2023  润新知