• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    golang中context的作用详解

    当一个goroutine可以启动其他goroutine,而这些goroutine可以启动其他goroutine,依此类推,则第一个goroutine应该能够向所有其它goroutine发送取消信号。

    上下文包的唯一目的是在goroutine之间执行取消信号,而不管它们如何生成。上下文的接口定义为:

    type Context interface {
     Deadline() (deadline time.Time, ok bool)
     Done() - chan struct{}
     Err() error
     Value(key interface{}) interface{}
    }

    要创建上下文,必须指定父上下文。两个内置上下文(背景和待办事项)用作顶级父上下文:

    var (
     background = new(emptyCtx)
     todo = new(emptyCtx)
    )
    func Background() Context {
     return background
    }
    func TODO() Context {
     return todo
    }

    背景,主要ü在主函数,初始化和测试代码的sed,是树结构中,根上下文,这是不能被取消的顶层语境。TODO,当您不知道要使用什么上下文时,可以使用它。它们本质上都是emptyCtx类型,都是不可取消的,没有固定的期限,也没有为Context赋任何值:键入emptyCtx int

    type emptyCtx int
    func (_ *emptyCtx) Deadline() (deadline time.Time, ok bool) {
     return
    }
    func (_ *emptyCtx) Done() - chan struct{} {
     return nil
    }
    func (_ *emptyCtx) Err() error {
     return nil
    }
    func (*emptyCtx) Value(key interface{}) interface{} {
     return nil
    }

    上下文包还具有几个常用功能:func WithCancel(父上下文)(ctx上下文,取消CancelFunc)func WithDeadline(父上下文,截止时间.Time)(上下文,CancelFunc)func WithTimeout(父上下文,超时时间。持续时间)(上下文,CancelFunc)func WithValue(父上下文,键,val接口{})上下文

    请注意,这些方法意味着可以一次继承上下文以实现其他功能,例如,使用WithCancel函数传入根上下文,它会创建一个子上下文,该子上下文具有取消上下文的附加功能,然后使用此方法将context(context01)作为父上下文,并将其作为第一个参数传递给WithDeadline函数,与子context(context01)相比,获得子context(context02),它具有一个附加功能,可在之后自动取消上下文最后期限。

    WithCancel

    对于通道,尽管通道也可以通知许多嵌套的goroutine退出,但通道不是线程安全的,而上下文是线程安全的。

    例如:

    package main
    import (
     "runtime"
     "fmt"
     "time"
     "context"
    )
    func monitor2(ch chan bool, index int) {
     for {
      select {
      case v := - ch:
       fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v)
       return
      default:
       fmt.Printf("monitor2: %v in progress...\n", index)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func monitor1(ch chan bool, index int) {
     for {
      go monitor2(ch, index)
      select {
      case v := - ch:
       // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
       fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v)
       return
      default:
       fmt.Printf("monitor1: %v in progress...\n", index)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func main() {
     var stopSingal chan bool = make(chan bool, 0)
     for i := 1; i = 5; i = i + 1 {
      go monitor1(stopSingal, i)
     }
     time.Sleep(1 * time.Second)
     // close all gourtines
     cancel()
     // waiting 10 seconds, if the screen does not display monitorX: xxxx in progress...>, all goroutines have been shut down
     time.Sleep(10 * time.Second)
     println(runtime.NumGoroutine())
     println("main program exit!!!!")
    }

    执行的结果是:

    monitor1: 5 in progress...
    monitor2: 5 in progress...
    monitor1: 2 in progress...
    monitor2: 2 in progress...
    monitor2: 1 in progress...
    monitor1: 1 in progress...
    monitor1: 4 in progress...
    monitor1: 3 in progress...
    monitor2: 4 in progress...
    monitor2: 3 in progress...
    monitor1: 4, the received channel value is: false, ending
    monitor1: 3, the received channel value is: false, ending
    monitor2: 2, the received channel value is: false, ending
    monitor2: 1, the received channel value is: false, ending
    monitor1: 1, the received channel value is: false, ending
    monitor2: 5, the received channel value is: false, ending
    monitor2: 3, the received channel value is: false, ending
    monitor2: 3, the received channel value is: false, ending
    monitor2: 4, the received channel value is: false, ending
    monitor2: 5, the received channel value is: false, ending
    monitor2: 1, the received channel value is: false, ending
    monitor1: 5, the received channel value is: false, ending
    monitor1: 2, the received channel value is: false, ending
    monitor2: 2, the received channel value is: false, ending
    monitor2: 4, the received channel value is: false, ending
    1
    main program exit!!!!

    这里使用一个通道向所有goroutine发送结束通知,但是这里的情况相对简单,如果在一个复杂的项目中,假设多个goroutine有某种错误并重复执行,则可以重复关闭或关闭该通道通道,然后向其写入值,从而触发运行时恐慌。这就是为什么我们使用上下文来避免这些问题的原因,以WithCancel为例:

    package main
    import (
     "runtime"
     "fmt"
     "time"
     "context"
    )
    func monitor2(ctx context.Context, number int) {
     for {
      select {
      case v := - ctx.Done():
       fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number,v)
       return
      default:
       fmt.Printf("monitor: %v in progress...\n", number)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func monitor1(ctx context.Context, number int) {
     for {
      go monitor2(ctx, number)
      select {
      case v := - ctx.Done():
       // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
       fmt.Printf("monitor: %v, the received channel value is: %v, ending\n", number, v)
       return
      default:
       fmt.Printf("monitor: %v in progress...\n", number)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func main() {
     var ctx context.Context = nil
     var cancel context.CancelFunc = nil
     ctx, cancel = context.WithCancel(context.Background())
     for i := 1; i = 5; i = i + 1 {
      go monitor1(ctx, i)
     }
     time.Sleep(1 * time.Second)
     // close all gourtines
     cancel()
     // waiting 10 seconds, if the screen does not display monitor: xxxx in progress>, all goroutines have been shut down
     time.Sleep(10 * time.Second)
     println(runtime.NumGoroutine())
     println("main program exit!!!!")
    }

    WithTimeout和WithDeadline

    WithTimeout和WithDeadline在用法和功能上基本相同,它们都表示上下文将在一定时间后自动取消,唯一的区别可以从函数的定义中看出,传递给WithDeadline的第二个参数是类型time.Duration类型,它是一个相对时间,表示取消超时后的时间。例:

    package main
    import (
     "runtime"
     "fmt"
     "time"
     "context"
    )
    func monitor2(ctx context.Context, index int) {
     for {
      select {
      case v := - ctx.Done():
       fmt.Printf("monitor2: %v, the received channel value is: %v, ending\n", index, v)
       return
      default:
       fmt.Printf("monitor2: %v in progress...\n", index)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func monitor1(ctx context.Context, index int) {
     for {
      go monitor2(ctx, index)
      select {
      case v := - ctx.Done():
       // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
       fmt.Printf("monitor1: %v, the received channel value is: %v, ending\n", index, v)
       return
      default:
       fmt.Printf("monitor1: %v in progress...\n", index)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func main() {
     var ctx01 context.Context = nil
     var ctx02 context.Context = nil
     var cancel context.CancelFunc = nil
     ctx01, cancel = context.WithCancel(context.Background())
     ctx02, cancel = context.WithDeadline(ctx01, time.Now().Add(1 * time.Second)) // If it's WithTimeout, just change this line to "ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)"
     defer cancel()
     for i := 1; i = 5; i = i + 1 {
      go monitor1(ctx02, i)
     }
     time.Sleep(5 * time.Second)
     if ctx02.Err() != nil {
      fmt.Println("the cause of cancel is: ", ctx02.Err())
     }
     println(runtime.NumGoroutine())
     println("main program exit!!!!")
    }

    WithValue

    一些必需的元数据也可以通过上下文传递,该上下文将附加到上下文中以供使用。元数据作为键值传递,但请注意,键必须具有可比性,并且值必须是线程安全的。

    package main
    import (
     "runtime"
     "fmt"
     "time"
     "context"
    )
    func monitor(ctx context.Context, index int) {
     for {
      select {
      case - ctx.Done():
       // this branch is only reached when the ch channel is closed, or when data is sent(either true or false)
       fmt.Printf("monitor %v, end of monitoring. \n", index)
       return
      default:
       var value interface{} = ctx.Value("Nets")
       fmt.Printf("monitor %v, is monitoring %v\n", index, value)
       time.Sleep(2 * time.Second)
      }
     }
    }
    func main() {
     var ctx01 context.Context = nil
     var ctx02 context.Context = nil
     var cancel context.CancelFunc = nil
     ctx01, cancel = context.WithCancel(context.Background())
     ctx02, cancel = context.WithTimeout(ctx01, 1 * time.Second)
     var ctx03 context.Context = context.WithValue(ctx02, "Nets", "Champion") // key: "Nets", value: "Champion"
    
     defer cancel()
     for i := 1; i = 5; i = i + 1 {
      go monitor(ctx03, i)
     }
     time.Sleep(5 * time.Second)
     if ctx02.Err() != nil {
      fmt.Println("the cause of cancel is: ", ctx02.Err())
     }
     println(runtime.NumGoroutine())
     println("main program exit!!!!")
    }

    关于上下文,还有一些注意事项:不要将Context存储在结构类型中,而是将Context明确传递给需要它的每个函数,并且Context应该是第一个参数。

    即使函数允许,也不要传递nil Context,或者如果您不确定要使用哪个Context,请传递context。不要将可能作为函数参数传递给上下文值的变量传递。

    到此这篇关于golang中context的作用的文章就介绍到这了,更多相关golang中context的作用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    您可能感兴趣的文章:
    • GoLang之使用Context控制请求超时的实现
    • golang通过context控制并发的应用场景实现
    • GOLANG使用Context实现传值、超时和取消的方法
    • GOLANG使用Context管理关联goroutine的方法
    • 深入Golang之context的用法详解
    上一篇:Golang实现http server提供压缩文件下载功能
    下一篇:Go 自定义error错误的处理方法
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

    golang中context的作用详解 golang,中,context,的,作用,