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