Go 并发编程基础:什么是上下文(上)

简介: 在开发过程中,也有这个上下文(Context)的概念,而且上下文也必不可少,缺少上下文,就不能获取完整的程序信息。那么什么是程序中的上下文呢?简单来说,就是在 API 之间或者函数调用之间,除了业务参数信息之外的额外信息。比如,服务器接收到客户端的 HTTP 请求之后,可以把客户端的 IP 地址和端口、客户端的身份信息、请求接收的时间、Trace ID 等信息放入到上下文中,这个上下文可以在后端的方法调用中传递。

1 Go 中的 Context

Golang 的上下文也是应用开发常用的并发控制工具。同理,上下文可以用于在程序中的 API 层或进程之间共享请求范围的数据,除此之外,Go 的 Context 库还提供取消信号(Cancel)以及超时机制(Timeout)。


Context 又被称为上下文,与 WaitGroup 不同的是,Context 对于派生 goroutine 有更强的控制力,可以管理多级的 goroutine。


但我们在 Go 中创建一个 goroutine 时,如果发生了一个错误,并且这个错误永远不会终止,而其他程序会继续进行。加入有一个不被调用的 goroutine 运行无限循环,如下所示:

package main
import "fmt"
func main() {
    dataCom := []string{"alex", "kyrie", "kobe"}
    go func(data []string) {
        // 模拟大量运算的死循环
    }(dataCom)
    // 其他代码正常执行
    fmt.Println("剩下的代码执行正常逻辑")
}


上面的例子并不完整,dataCom goroutine 可能会也可能不会成功处理数据。它可能会进入无限循环或导致错误。我们的其余代码将不知道发生了什么。


有多种方法可以解决这个问题。其中之一是使用通道向我们的主线程发送一个信号,表明这个 goroutine 花费的时间太长,应该取消它。

package main
import (
  "fmt"
  "time"
)
func main() {
  stopChannel := make(chan bool)
  dataCom := []string{"alex", "kyrie", "kobe"}
  go func(stopChannel chan bool) {
    go func(data []string) {
      // 大量的计算
    }(dataCom)
    for range time.After(2 * time.Second) {
      fmt.Println("此操作运行时间过长,取消中")
      stopChannel <- true
    }
  }(stopChannel)
  <-stopChannel
  // 其他代码正常执行
  fmt.Println("剩下的代码执行正常逻辑")
}


上面的逻辑很简单。我们正在使用一个通道向我们的主线程发出这个 goroutine 花费的时间太长的信号。但是同样的事情可以用 context 来完成,这正是 context 包存在的原因。

package main
import (
  "context"
  "fmt"
  "time"
)
func main() {
  ctx, cancel := context.WithTimeout(context.Background(), time.Second*2)
  defer cancel()
  dataCom := []string{"alex", "kyrie", "kobe"}
  go func() {
    go func(data []string) {
      // 大量的计算
    }(dataCom)
    for range time.After(2 * time.Second) {
      fmt.Println("此操作运行时间过长,取消中")
      cancel()
      return
    }
  }()
  select {
  case <-ctx.Done():
    fmt.Println("上下文被取消")
  }
}

2 Context 接口

Context 接口定义:

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


Context 接口定义了 4 个方法:


  • Deadline():  返回取消此上下文的时间 deadline(如果有)。如果未设置 deadline 时,则返回 ok==false,此时 deadline 为一个初始值的 time.Time 值。后续每次调用这个对象的 Deadline 方法时,都会返回和第一次调用相同的结果。
  • Done() : 返回一个用于探测 Context 是否取消的 channel,当 Context 取消会自动将该 channel 关闭,如果该 Context 不能被永久取消,该函数返回 nil。例如 context.Background();如果 Done 被 close,Err 方法会返回 Done 被 close 的原因。
  • Err(): 该方法会返回 context 被关闭的原因,关闭原因由 context 实现控制,不需要用户设置;如果 Done() 尚未关闭,则 Err() 返回 nil
  • Value() : 在树状分布的 goroutine 之间共享数据,用 map 键值的工作方法,通过 key 值查询 value。


每次创建新上下文时,都会得到一个符合此接口的类型。上下文的真正实现隐藏在这个包和这个接口后面。这些是您可以创建的工厂类型的上下文:


  1. context.TODO
  2. context.Background
  3. context.WithCancel
  4. context.WithValue
  5. context.WithTimeout
  6. context.WithDeadline
相关文章
|
2月前
|
Go
go语言并发编程(五) ——Context
go语言并发编程(五) ——Context
|
2月前
|
存储 缓存 Go
Go语言并发编程(三)——初窥管道
Go语言并发编程(三)——初窥管道
|
3月前
|
安全 Go 调度
Go语言中的并发编程
Go语言自带了强大的并发编程能力,它的协程机制可以让程序轻松地实现高并发。本文将从并发编程的基础概念出发,介绍Go语言中的协程机制、通道和锁等相关知识点,帮助读者更好地理解并发编程在Go语言中的实践应用。
|
11天前
|
存储 安全 Go
Go 并发编程精粹:掌握通道(channels)的艺术
Go 并发编程精粹:掌握通道(channels)的艺术
|
11天前
|
Go 开发者
使用Go语言进行高效并发编程
【8月更文挑战第9天】Go语言的并发模型以其简洁和高效著称,通过goroutines和channels,开发者可以轻松地编写出高性能的并发程序。此外,Go标准库还提供了丰富的并发原语和工具,如WaitGroup和Context,进一步简化了并发编程的复杂性。掌握Go语言的并发编程技巧,对于开发高性能、高并发的应用至关重要。希望本文能帮助你更好地理解和使用Go语言进行并发编程。
|
12天前
|
Go API
Go 利用上下文进行并发计算
Go 利用上下文进行并发计算
|
21天前
|
JSON Java Serverless
函数计算产品使用问题之如何使用Go SDK从HTTP上下文中提取JSON数据
函数计算产品作为一种事件驱动的全托管计算服务,让用户能够专注于业务逻辑的编写,而无需关心底层服务器的管理与运维。你可以有效地利用函数计算产品来支撑各类应用场景,从简单的数据处理到复杂的业务逻辑,实现快速、高效、低成本的云上部署与运维。以下是一些关于使用函数计算产品的合集和要点,帮助你更好地理解和应用这一服务。
|
2月前
|
Go
如何在Go中进行文件操作以及如何使用协程来实现并发编程
如何在Go中进行文件操作以及如何使用协程来实现并发编程
28 2
|
2月前
|
Linux Go 索引
go语言并发编程(四) ——再探管道
go语言并发编程(四) ——再探管道
|
2月前
|
Go 开发者
探索Go语言的并发编程模型
通过实例详细介绍了Go语言中的并发编程模型,包括goroutine、channel的基本使用和最佳实践。深入剖析如何利用Go的并发特性提高程序性能和效率,适用于初学者和有一定经验的开发者。