当前位置:网站首页>Go 并发编程基础:什么是上下文
Go 并发编程基础:什么是上下文
2022-07-22 22:41:00 【InfoQ】
0 前言
1 Go 中的 Context
package main
import "fmt"
func main() {
dataCom := []string{"alex", "kyrie", "kobe"}
go func(data []string) {
// 模拟大量运算的死循环
}(dataCom)
// 其他代码正常执行
fmt.Println("剩下的代码执行正常逻辑")
}
dataCompackage 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("剩下的代码执行正常逻辑")
}
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 接口
type Context interface {
Deadline() (deadline time.Time, ok bool)
Done <-chan struct{}
Err() error
Value(key interface{}) interface{}
}
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。
context.TODO
context.Background
context.WithCancel
context.WithValue
context.WithTimeout
context.WithDeadline
3 Context Tree

4 创建上下文
4.1 上下文创建函数
BackgroundTODOcontext.Background() ctx Context
ctx, cancel := context.Background()
context.TODO() ctx Context
ctx, cancel := context.TODO()
context.WithValue(parent Context, key, val interface{}) Context
WithValuetype valueCtx struct {
Context
key, val interface{}
}
interface{}struct{}package main
import (
"context"
"fmt"
)
type contextKey string
func main() {
var authToken contextKey = "auth_token"
ctx := context.WithValue(context.Background(), authToken, "Hello123456")
fmt.Println(ctx.Value(authToken))
}
$ go run .
Hello123456
func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
Donecancelcancelpackage main
import (
"context"
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().UnixNano())
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
for char := range randomCharGenerator(ctx) {
generatedChar := string(char)
fmt.Printf("%v\n", generatedChar)
if generatedChar == "o" {
break
}
}
}
func randomCharGenerator(ctx context.Context) <-chan int {
char := make(chan int)
seedChar := int('a')
go func() {
for {
select {
case <-ctx.Done():
fmt.Printf("found %v", seedChar)
return
case char <- seedChar:
seedChar = 'a' + rand.Intn(26)
}
}
}()
return char
}
$ go run .
a
m
q
c
l
t
o
func WithDeadline(parent Context, d time.Time) (Context, CancelFunc)
package main
import (
"bufio"
"context"
"fmt"
"log"
"os"
"time"
)
func main() {
// context with deadline after 2 millisecond
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(2*time.Millisecond))
defer cancel()
lineRead := make(chan string)
var fileName = "sample-file.txt"
file, err := os.Open(fileName)
if err != nil {
log.Fatalf("failed opening file: %s", err)
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
// goroutine to read file line by line and passing to channel to print
go func() {
for scanner.Scan() {
lineRead <- scanner.Text()
}
close(lineRead)
file.Close()
}()
outer:
for {
// printing file line by line until deadline is reached
select {
case <-ctx.Done():
fmt.Println("process stopped. reason: ", ctx.Err())
break outer
case line := <-lineRead:
fmt.Println(line)
}
}
}
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc) {
// 当前时间+timeout就是deadline
return WithDeadline(parent, time.Now().Add(timeout))
}
WithDeadline(parent, time.Now().Add(timeout))package main
import (
"bufio"
"context"
"fmt"
"log"
"os"
"time"
)
func main() {
// context with deadline after 2 millisecond
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Millisecond)
defer cancel()
lineRead := make(chan string)
var fileName = "sample-file.txt"
file, err := os.Open(fileName)
if err != nil {
log.Fatalf("failed opening file: %s", err)
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanLines)
// goroutine to read file line by line and passing to channel to print
go func() {
for scanner.Scan() {
lineRead <- scanner.Text()
}
close(lineRead)
file.Close()
}()
outer:
for {
// printing file line by line until deadline is reached
select {
case <-ctx.Done():
fmt.Println("process stopped. reason: ", ctx.Err())
break outer
case line := <-lineRead:
fmt.Println(line)
}
}
}
package main
import (
"context"
"fmt"
"time"
)
func main() {
c := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c <- "one"
}()
ctx1 := context.Context(context.Background())
ctx2, cancel2 := context.WithTimeout(ctx1, 2*time.Second)
ctx3, cancel3 := context.WithTimeout(ctx2, 10*time.Second) // derives from ctx2
ctx4, cancel4 := context.WithTimeout(ctx2, 3*time.Second) // derives from ctx2
ctx5, cancel5 := context.WithTimeout(ctx4, 5*time.Second) // derives from ctx4
cancel2()
defer cancel3()
defer cancel4()
defer cancel5()
select {
case <-ctx3.Done():
fmt.Println("ctx3 closed! error: ", ctx3.Err())
case <-ctx4.Done():
fmt.Println("ctx4 closed! error: ", ctx4.Err())
case <-ctx5.Done():
fmt.Println("ctx5 closed! error: ", ctx5.Err())
case msg := <-c:
fmt.Println("received", msg)
}
}
package main
import (
"context"
"fmt"
)
func main() {
ctx := context.Background()
_, ok := ctx.Deadline()
if !ok {
fmt.Println("no dealine is set")
}
done := ctx.Done()
if done == nil {
fmt.Println("channel is nil")
}
}
4.2 Context 使用规范
- 不要将上下文存储在结构类型中;相反,将 Context 显式传递给需要它的每个函数。 Context 应该是第一个参数,通常命名为 ctx。
func DoSomething(ctx context.Context, arg Arg) error {
// ... use ctx ...
}
- 不要传递 nil 上下文,即使函数允许。如果不确定要使用哪个 Context,请传递
context.TODO或使用context.Background()创建一个空的上下文对象。
- 仅使用上下文传递请求范围的数据。不要传递应该使用函数参数传递的数据。
- 始终寻找 goroutine 泄漏并有效地使用上下文来避免这种情况。
- 如果父上下文的 Done 通道关闭,它最终将关闭所有派生的 Done 通道(所有后代)
- 上下文只是临时做函数之间的上下文传透,不能持久化上下文
- key 的类型不应该是字符串类型或者其它内建类型,否则容易在包之间使用 Context 时候产生冲突。使用 WithValue 时,key 的类型应该是自己定义的类型。
4.3 Context 使用场景
- 上下文信息传递 (request-scoped),比如处理 http 请求、在请求处理链路上传递信息;
- 控制子 goroutine 的运行;
- 超时控制的方法调用;
- 可以取消的方法调用。
5 总结
- Go Concurrency Patterns: Context
- Simplifying context in Go
边栏推荐
猜你喜欢

c语言十进制数转二进制数

Learn these SketchUp skills and improve work efficiency by half

Web资源共享

U盘被格式化数据能恢复吗,U盘被格式化了怎样恢复

Redis profile

MSG | 开源与结缘,昇思携梦前行!

PostgreSQL database master-slave deployment master database suspended restore master database

Understand the interrupt system in STM32 in simple terms -- from principle to simple engineering examples -- nanny level tutorial

C language decimal number to binary number

promise(二)
随机推荐
三种缓存策略:Cache Aside 策略、Read/Write Through 策略、Write Back 策略
The cubic root of a number
EMQX v4.4.5 发布:新增排他订阅及 MQTT 5.0 发布属性支持
Cloud computing may become a new inflection point in the era? From which point can we see?
深入浅出地理解STM32中的中断系统——从原理到简单工程示例——保姆级教程
Easily take you to the gate of turtle drawing
Introduction to JVM monitoring tools jstack, jconsole, Jinfo, jmap, JDB, jstat
大咖訪談 | 開源社區裏各種奇怪的現狀——夜天之書陳梓立tison
appendToFile追加失败
Use the same interface to realize different login methods
Jmeter分布式压测
Networkx visualizes graphs
Hcip --- BGP comprehensive experiment
笔者认为,元宇宙与互联网的发展逻辑在某种成都上是截然不同的
wps数据拆分
PIP update a package
论文阅读:The Perfect Match: 3D Point Cloud Matching with Smoothed Densities
网络参数管理
php数组下标是不是只能从0开始
大咖访谈 | 开源社区里各种奇怪的现状——夜天之书陈梓立tison