zoukankan      html  css  js  c++  java
  • 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深入理解

  • 相关阅读:
    怎样从外网访问内网Django?
    怎样从外网访问内网Jboss?
    怎样从外网访问内网php-fpm?
    python中关于发邮件的示例
    python中关于局部变量与全局变量的认识
    python实现二分查找与冒泡排序
    自动化测试框架中关于selenium api的二次封装
    python 的日志相关应用
    python中关于字符串的操作
    EBS 物料单位换算
  • 原文地址:https://www.cnblogs.com/lanyangsh/p/10890241.html
Copyright © 2011-2022 走看看