Golang

关注公众号 jb51net

关闭
首页 > 脚本专栏 > Golang > Golang Context包使用

Golang并发编程中Context包的使用与并发控制

作者:Linke-

Golang的context包提供了在并发编程中传递取消信号、超时控制和元数据的功能,本文就来介绍一下Golang并发编程中Context包的使用与并发控制,感兴趣的可以了解一下

一、简介

在并发编程中,任务管理和资源控制是非常重要的,而 Golang 的 context 包 为我们提供了一种优雅的方式来传递取消信号超时控制Context 用于在多个 Goroutine 之间传递上下文信息,避免 Goroutine 无法按需停止而导致资源浪费。

本篇博客将详细介绍 context 包的用法,并通过实例讲解如何在超时、取消任务多 Goroutine 协作场景中使用它。

二、Context 的基本概念

Context 是一种携带取消信号、截止时间(超时)和元数据的上下文对象,主要用于父 Goroutine 与子 Goroutine 的协作。它通过层级化的结构来管理多个并发任务。

1. context 包常用函数

三、Context 的基本用法

1. WithCancel:取消任务的上下文

示例:使用 WithCancel 取消 Goroutine

package main

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

func worker(ctx context.Context, id int) {
    for {
        select {
        case <-ctx.Done(): // 接收取消信号
            fmt.Printf("Worker %d stopped\n", id)
            return
        default:
            fmt.Printf("Worker %d is working...\n", id)
            time.Sleep(time.Second)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background()) // 创建可取消的上下文

    for i := 1; i <= 3; i++ {
        go worker(ctx, i)
    }

    time.Sleep(3 * time.Second) // 模拟主 Goroutine 的其他工作
    fmt.Println("Cancelling all workers...")
    cancel() // 发送取消信号

    time.Sleep(1 * time.Second) // 等待所有 Goroutine 退出
    fmt.Println("All workers stopped.")
}

输出:

Worker 1 is working...
Worker 2 is working...
Worker 3 is working...
...
Cancelling all workers...
Worker 1 stopped
Worker 2 stopped
Worker 3 stopped
All workers stopped.

解析:

context.WithCancel 创建的上下文可以通过调用 cancel() 发送取消信号,从而优雅地停止所有子 Goroutine。

四、超时控制:WithTimeout 和 WithDeadline

1. 使用 WithTimeout 控制任务超时

示例:在 2 秒内完成任务,否则超时退出

package main

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

func worker(ctx context.Context) {
    select {
    case <-time.After(3 * time.Second): // 模拟长时间任务
        fmt.Println("Task completed")
    case <-ctx.Done(): // 接收超时信号
        fmt.Println("Task timed out")
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) // 设置 2 秒超时
    defer cancel() // 确保资源释放

    go worker(ctx)

    time.Sleep(4 * time.Second) // 等待任务完成或超时
}

输出:

Task timed out

解析:

2. 使用 WithDeadline 设定截止时间

WithDeadline 和 WithTimeout 类似,只是使用具体的时间点来控制超时。

五、传递上下文中的数据:WithValue

有时,我们需要在多个 Goroutine 之间传递一些元数据。WithValue 允许我们将键值对存入上下文,并在子 Goroutine 中访问。

示例:传递用户信息

package main

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

func greetUser(ctx context.Context) {
    if user, ok := ctx.Value("user").(string); ok {
        fmt.Printf("Hello, %s!\n", user)
    } else {
        fmt.Println("No user found.")
    }
}

func main() {
    ctx := context.WithValue(context.Background(), "user", "Alice") // 在上下文中存入用户信息
    go greetUser(ctx)

    time.Sleep(1 * time.Second) // 确保 Goroutine 执行完毕
}

输出:

Hello, Alice!

解析:

WithValue 允许我们为上下文设置键值对,便于在多 Goroutine 间传递数据。

注意:

不建议用 WithValue 传递重要的控制信息,例如取消信号或超时。

六、Context 的应用场景

七、完整示例:多任务协作控制

示例:启动多个任务,随时可取消所有任务

package main

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

func worker(ctx context.Context, id int, wg *sync.WaitGroup) {
    defer wg.Done()

    for {
        select {
        case <-ctx.Done():
            fmt.Printf("Worker %d stopped\n", id)
            return
        default:
            fmt.Printf("Worker %d is processing...\n", id)
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    var wg sync.WaitGroup
    ctx, cancel := context.WithCancel(context.Background()) // 创建上下文

    for i := 1; i <= 3; i++ {
        wg.Add(1)
        go worker(ctx, i, &wg)
    }

    time.Sleep(2 * time.Second)
    fmt.Println("Cancelling all workers...")
    cancel() // 取消所有任务

    wg.Wait() // 等待所有任务完成
    fmt.Println("All workers stopped.")
}

输出:

Worker 1 is processing...
Worker 2 is processing...
Worker 3 is processing...
...
Cancelling all workers...
Worker 1 stopped
Worker 2 stopped
Worker 3 stopped
All workers stopped.

八、小结

到此这篇关于Golang并发编程中Context包的使用与并发控制的文章就介绍到这了,更多相关Golang Context包使用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家! 

您可能感兴趣的文章:
阅读全文