一文读懂Golang中的Context应用场景(含案例)

context的应用场景?

context 包是 Go 语言标准库提供的一个重要工具,用于在多个 Goroutine 之间传递取消信号、超时信息、截止时间以及请求范围的值等。它在编写并发代码时非常有用,特别是在处理请求的情况下。以下是一些 context 的主要应用场景:

  1. 取消信号传递
    • 当一个 Goroutine 启动了一些子 Goroutine 来执行任务时,可以使用 context 来向这些 Goroutine 发送取消信号,以便在主 Goroutine 接收到取消信号后,通知所有子 Goroutine 停止正在执行的任务。这种方式可以有效地控制 Goroutine 的生命周期,避免资源泄漏。
  1. 超时控制
    • 在网络请求或者执行一些 IO 操作时,我们希望能够在一定的时间内完成操作,如果超过了预期的时间,就放弃该操作或者进行一些处理。context 可以与 time 包结合使用,设置超时时间,当超时发生时,相关操作可以被取消或者中断。
  1. 截止时间
    • 类似于超时控制,但不同的是,截止时间通常是一个绝对时间点,而不是相对于当前时间的一段时间。例如,某个任务必须在某个特定的截止时间之前完成,否则就没有意义或者需要进行其他处理。
  1. 请求范围的值传递
    • 在一个请求处理流程中,可能需要在多个 Goroutine 中访问同一个请求相关的值(例如请求 ID、用户认证信息等)。context 可以用来在 Goroutine 之间传递这些值,而不需要在每个函数中手动传递这些值。
  1. 控制并发请求
    • 在进行并发请求时,可能需要控制同时进行的请求数量,以避免过载服务器或者服务提供商的限制。context 可以在每个请求中进行计数或者限制,并根据情况决定是否进行并发请求。

总之,context 包提供了一种优雅的方式来管理并发代码中的取消、超时、截止时间和值传递等问题,可以帮助开发者编写更加健壮和可靠的并发程序。

对应的代码例子:

1. 取消信号传递:

go 复制代码
package main

import (
	"context"
	"fmt"
	"time"
)

func worker(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			fmt.Println("Worker received cancellation signal.")
			return
		default:
			// Simulate some work
			time.Sleep(500 * time.Millisecond)
			fmt.Println("Working...")
		}
	}
}

func main() {
	parentCtx, cancel := context.WithCancel(context.Background())
	go worker(parentCtx)

	// Simulate main program execution
	time.Sleep(2 * time.Second)

	// Cancel the context to stop the worker
	cancel()

	// Wait for the worker to finish
	time.Sleep(1 * time.Second)
}

2. 超时控制:

go 复制代码
package main

import (
	"context"
	"fmt"
	"time"
)

func operationWithTimeout(ctx context.Context) {
	select {
	case <-time.After(3 * time.Second): // Simulate some long operation
		fmt.Println("Operation completed.")
	case <-ctx.Done():
		fmt.Println("Operation canceled due to timeout.")
	}
}

func main() {
	timeoutCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()

	operationWithTimeout(timeoutCtx)
}

3. 截止时间:

go 复制代码
package main

import (
	"context"
	"fmt"
	"time"
)

func operationWithDeadline(ctx context.Context) {
	deadline, ok := ctx.Deadline()
	if ok {
		fmt.Printf("Operation must be completed before: %s\n", deadline)
	} else {
		fmt.Println("No specific deadline for the operation.")
	}

	// Simulate some operation
	time.Sleep(2 * time.Second)

	select {
	case <-ctx.Done():
		fmt.Println("Operation canceled due to context deadline.")
	default:
		fmt.Println("Operation completed within the deadline.")
	}
}

func main() {
	deadline := time.Now().Add(5 * time.Second)
	deadlineCtx, cancel := context.WithDeadline(context.Background(), deadline)
	defer cancel()

	operationWithDeadline(deadlineCtx)
}

4. 请求范围的值传递:

go 复制代码
package main

import (
	"context"
	"fmt"
	"sync"
)

func processRequest(ctx context.Context, requestID int) {
	// Accessing request-scoped value from the context
	userID, ok := ctx.Value("userID").(int)
	if !ok {
		fmt.Println("Failed to get userID from context.")
		return
	}

	fmt.Printf("Processing request %d for user %d\n", requestID, userID)
}

func main() {
	// Creating a parent context with a request-scoped value
	parentCtx := context.WithValue(context.Background(), "userID", 123)

	var wg sync.WaitGroup

	// Simulating multiple requests
	for i := 1; i <= 3; i++ {
		wg.Add(1)
		go func(requestID int) {
			// Creating a child context for each request
			childCtx := context.WithValue(parentCtx, "requestID", requestID)
			processRequest(childCtx, requestID)
			wg.Done()
		}(i)
	}

	wg.Wait()
}

5. 控制并发请求:

go 复制代码
package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

type limitKey struct{}

func WithLimit(parent context.Context, limit int) (context.Context, context.CancelFunc) {
	// Use a buffered channel with the limit as its capacity
	sem := make(chan struct{}, limit)

	// Create a new context with a cancellation function
	ctx, cancel := context.WithCancel(parent)

	// Replace the value of the key with the semaphore
	return context.WithValue(ctx, limitKey{}, sem), cancel
}

func performRequest(ctx context.Context, requestID int) {
	// Access semaphore from context
	sem, _ := ctx.Value(limitKey{}).(chan struct{})

	// Acquire the semaphore
	sem <- struct{}{}
	defer func() { <-sem }() // Release the semaphore after request completes

	// Simulating some request processing
	// doing
	time.Sleep(1 * time.Second)
	fmt.Printf("Request %d completed.\n", requestID)
}

func main() {
	requests := []int{1, 2, 3, 4, 5}

	// Creating a context with a limit on concurrent requests
	concurrentCtx, cancel := WithLimit(context.Background(), 2)
	defer cancel()

	var wg sync.WaitGroup

	for _, requestID := range requests {
		wg.Add(1)
		go func(id int) {
			// Check if the context is canceled before performing the request
			select {
			case <-concurrentCtx.Done():
				fmt.Printf("Request %d canceled due to context cancellation.\n", id)
			default:
				performRequest(concurrentCtx, id)
			}
			wg.Done()
		}(requestID)
	}

	wg.Wait()
}
相关推荐
无心水3 小时前
1、Go语言工作区和GOPATH实战指南:从项目初始化到部署
开发语言·后端·架构·golang·go·gopath·go mod init
BlockChain88815 小时前
Solidity 实战【二】:手写一个「链上资金托管合约」
go·区块链
BlockChain8881 天前
Solidity 实战【三】:重入攻击与防御(从 0 到 1 看懂 DAO 事件)
go·区块链
剩下了什么1 天前
Gf命令行工具下载
go
地球没有花1 天前
tw引发的对redis的深入了解
数据库·redis·缓存·go
BlockChain8882 天前
字符串最后一个单词的长度
算法·go
龙井茶Sky2 天前
通过higress AI统计插件学gjson表达式的分享
go·gjson·higress插件
宇宙帅猴3 天前
【Ubuntu踩坑及解决方案(一)】
linux·运维·ubuntu·go
SomeBottle4 天前
【小记】解决校园网中不同单播互通子网间 LocalSend 的发现问题
计算机网络·go·网络编程·学习笔记·计算机基础
且去填词4 天前
深入理解 GMP 模型:Go 高并发的基石
开发语言·后端·学习·算法·面试·golang·go