go 的 Context
Laiyong Wang Lv6

context包提供了一种方式来管理多个goroutine之间的截止时间、取消信号和请求范围值。context常用于在多个goroutine之间传递取消信号或截止时间,以便能够及时终止或超时处理操作。它的主要作用是控制并发操作的生命周期。

描述

context包提供了Context类型和与之相关的函数,用于传递截止日期、取消信号和其他请求范围值。Context类型是一个接口,它定义了四个方法:

  • Deadline() (deadline time.Time, ok bool):返回设置的截止时间和一个布尔值,表示是否设置了截止时间。
  • Done() <-chan struct{}:返回一个通道,当Context被取消或超时时,通道会被关闭。
  • Err() error:返回Context被取消的原因:CanceledDeadlineExceeded
  • Value(key interface{}) interface{}:返回与key关联的值。

作用

  • 取消信号传递:允许在不同的goroutine之间传递取消信号,从而能够在需要时取消操作。
  • 超时控制:设置截止时间,自动在指定时间后取消操作。
  • 传递请求范围的值:在整个请求的生命周期中传递与请求相关的数据。

用法

通常,context会与context.Background()context.TODO()一起使用来创建根Context,然后通过context.WithCancelcontext.WithDeadlinecontext.WithTimeout派生出子Context

实际例子

以下是一个实际例子,展示了如何在HTTP服务器中使用context来控制请求的超时和取消操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package main

import (
"context"
"fmt"
"net/http"
"time"
)

func main() {
http.HandleFunc("/hello", helloHandler)
http.ListenAndServe(":8080", nil)
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
// 设置一个超时的 context
ctx, cancel := context.WithTimeout(r.Context(), 2*time.Second)
defer cancel()

// 模拟一个需要时间的操作
select {
case <-time.After(3 * time.Second):
fmt.Fprintln(w, "Hello, World!")
case <-ctx.Done():
// 如果操作超时或被取消
http.Error(w, "Request canceled or timed out", http.StatusRequestTimeout)
}
}
  1. helloHandler接收一个HTTP请求,并创建一个带有2秒超时的context
  2. 使用select语句来等待模拟的操作完成或context超时。
  3. 如果操作在2秒内完成,则返回“Hello, World!”。否则,返回一个超时错误。

通过这种方式,context帮助我们处理长时间运行的操作,并在需要时取消操作,避免资源浪费和响应延迟。

$li-margin-bottom = 0.8rem $post-tool-button-width = 2.5rem .post-tools-container { padding-top var(--component-gap) .post-tools-list { li { margin-bottom $li-margin-bottom &:last-child { margin-bottom 0 } } li.tools-item { position relative box-sizing border-box width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.2rem background var(--background-color-1) border-radius 50% box-shadow 2px 2px 5px var(--shadow-color) cursor pointer &:hover { box-shadow 2px 2px 8px var(--shadow-hover-color) } i { color var(--text-color-3) } &:hover { color var(--background-color-1) background var(--primary-color) i { color var(--background-color-1) !important } } &.toggle-show-toc { display none } &.go-to-comments { .post-comments-count { position absolute top 0 right -1rem display none align-items center justify-content center box-sizing border-box min-width 1.1rem height 1.1rem padding 0 0.2rem color var(--badge-color) font-size 12px background var(--badge-background-color) border-radius 0.4rem +keep-tablet() { display none !important } } } } li.status-item { width $post-tool-button-width height $post-tool-button-width color var(--text-color-3) font-size 1.6rem cursor pointer &.post-lock { cursor default .fa-lock-open { display none color var(--keep-success-color) } &.decrypt { cursor pointer .fa-lock-open { display block } .fa-lock { display none } } } } } }