一文读懂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()
}
相关推荐
童先生37 分钟前
Go 项目中实现类似 Java Shiro 的权限控制中间件?
开发语言·go
幼儿园老大*2 小时前
走进 Go 语言基础语法
开发语言·后端·学习·golang·go
架构师那点事儿7 小时前
golang 用unsafe 无所畏惧,但使用不得到会panic
架构·go·掘金技术征文
于顾而言1 天前
【笔记】Go Coding In Go Way
后端·go
qq_172805591 天前
GIN 反向代理功能
后端·golang·go
follycat1 天前
2024强网杯Proxy
网络·学习·网络安全·go
OT.Ter1 天前
【力扣打卡系列】单调栈
算法·leetcode·职场和发展·go·单调栈
探索云原生1 天前
GPU 环境搭建指南:如何在裸机、Docker、K8s 等环境中使用 GPU
ai·云原生·kubernetes·go·gpu
OT.Ter1 天前
【力扣打卡系列】移动零(双指针)
算法·leetcode·职场和发展·go
码财小子2 天前
k8s 集群中 Golang pprof 工具的使用
后端·kubernetes·go