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
相关文章
|
6天前
|
人工智能 Go 调度
掌握Go并发:Go语言并发编程深度解析
掌握Go并发:Go语言并发编程深度解析
|
28天前
|
安全 Go
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
掌握Go语言:Go语言通道,并发编程的利器与应用实例(20)
|
3月前
|
并行计算 算法 安全
通过三个例子,学习 Go 语言并发编程的利器 - goroutine
通过三个例子,学习 Go 语言并发编程的利器 - goroutine
42 0
|
4月前
|
Go
通道多路复用:Go语言并发编程的黄金法则
通道多路复用:Go语言并发编程的黄金法则
36 0
|
4月前
|
安全 Go
Go新手步步为赢:并发编程通信指南
Go新手步步为赢:并发编程通信指南
25 0
|
5天前
|
程序员 Go
Golang深入浅出之-Select语句在Go并发编程中的应用
【4月更文挑战第23天】Go语言中的`select`语句是并发编程的关键,用于协调多个通道的读写。它会阻塞直到某个通道操作可行,执行对应的代码块。常见问题包括忘记初始化通道、死锁和忽视`default`分支。要解决这些问题,需确保通道初始化、避免死锁并添加`default`分支以处理无数据可用的情况。理解并妥善处理这些问题能帮助编写更高效、健壮的并发程序。结合使用`context.Context`和定时器等工具,可提升`select`的灵活性和可控性。
18 2
|
2月前
|
缓存 安全 Java
go并发编程
go的GMP并发模型,让go天然支持高并发,先了解一下GMP模型吧 GMP G协程,M工作线程、P处理器,M必须持有P才可以执行G P维护着一个协程G队列,P依次将G调度到M中运行 if M0中G0发生系统调用,M0将释放P,冗余的M1获取P,继续执行P队列中剩余的G。(只要P不空闲就充分利用了CPU) G0系统调用结束后,如果有空闲的P,则获取P继续执行G0,否则将G0放入全局队列,M0进入缓存池睡眠。(全局队列中的G主要来自从系统调用中恢复的G) 下面介绍一下编程常用的同步(synchronize)原语 互斥锁 mutex rwmutex,要了解自旋和饥饿模式 自旋最多4次,cpu核
32 1
|
1天前
|
Go
【Go语言专栏】Go语言的并发编程进阶:互斥锁与条件变量
【4月更文挑战第30天】本文探讨了Go语言中的互斥锁(Mutex)和条件变量(Condition Variable)在并发编程中的应用。互斥锁用于保护共享资源,防止多goroutine同时访问,通过Lock和Unlock进行控制,需注意避免死锁。条件变量则允许goroutine在条件满足时被唤醒,常与互斥锁结合使用以提高效率。了解和掌握这些同步原语能提升Go并发程序的性能和稳定性。进一步学习可参考Go官方文档和并发模式示例。
|
1天前
|
存储 Go
【Go 语言专栏】Go 语言的并发编程基础:goroutines 与 channels
【4月更文挑战第30天】Go 语言的并发编程基于goroutines和channels。Goroutines是轻量级线程,低成本并发执行。创建goroutine只需在函数调用前加`go`。Channels作为goroutines间通信和同步的桥梁,分无缓冲和有缓冲两种,可用`make`创建。结合使用goroutines和channels,可实现数据传递和同步,如通过无缓冲channels实现任务同步,或通过有缓冲channels传递数据。注意避免死锁、资源竞争,合理使用缓冲,以发挥Go并发优势。
|
5天前
|
Go 开发者
Golang深入浅出之-Go语言上下文(context)包:处理取消与超时
【4月更文挑战第23天】Go语言的`context`包提供`Context`接口用于处理任务取消、超时和截止日期。通过传递`Context`对象,开发者能轻松实现复杂控制流。本文解析`context`包特性,讨论常见问题和解决方案,并给出代码示例。关键点包括:1) 确保将`Context`传递给所有相关任务;2) 根据需求选择适当的`Context`创建函数;3) 定期检查`Done()`通道以响应取消请求。正确使用`context`包能提升Go程序的控制流管理效率。
14 1