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

 更新时间:2024年11月18日 10:38:09   作者:Linke-  
Golang的context包提供了在并发编程中传递取消信号、超时控制和元数据的功能,本文就来介绍一下Golang并发编程中Context包的使用与并发控制,感兴趣的可以了解一下

一、简介

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

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

二、Context 的基本概念

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

1. context 包常用函数

  • context.Background():创建根上下文,通常用于程序入口。
  • context.TODO():占位符上下文,表示未来会替换为实际上下文。
  • context.WithCancel(parent Context):创建带取消功能的子上下文。
  • context.WithTimeout(parent Context, timeout time.Duration):创建带超时功能的子上下文。
  • context.WithDeadline(parent Context, deadline time.Time):基于指定的截止时间创建上下文。
  • context.WithValue(parent Context, key, value interface{}):传递携带额外数据的上下文。

三、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

解析:

  • 通过 context.WithTimeout 创建的上下文,2 秒内未完成任务时自动发送取消信号。
  • 超时上下文可避免 Goroutine 无限期运行,帮助更好地管理资源。

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 的应用场景

  • API 请求的超时控制:确保 HTTP 请求不会无限期等待。
  • 任务取消:当用户主动取消某个操作时,通知相关的 Goroutine 停止工作。
  • 传递元数据:例如在服务链路中传递用户身份、请求 ID 等信息。

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

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

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.

八、小结

  • Context 用于并发控制:在 Goroutine 中传递取消信号、超时信号或携带元数据。
  • 超时控制与资源管理:使用 WithTimeout 和 WithCancel 及时终止任务,避免资源浪费。
  • 多 Goroutine 协作:通过 Context 实现多个 Goroutine 之间的优雅通信。

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

相关文章

  • GoLang的sync.WaitGroup与sync.Once简单使用讲解

    GoLang的sync.WaitGroup与sync.Once简单使用讲解

    sync.WaitGroup类型,它比通道更加适合实现这种一对多的goroutine协作流程。WaitGroup是开箱即用的,也是并发安全的。同时,与之前提到的同步工具一样,它一旦被真正的使用就不能被复制了
    2023-01-01
  • Golang Http请求返回结果处理

    Golang Http请求返回结果处理

    本文主要介绍了Golang Http请求返回结果处理,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2022-08-08
  • go语言Timer计时器的用法示例详解

    go语言Timer计时器的用法示例详解

    Go语言的标准库里提供两种类型的计时器Timer和Ticker。这篇文章通过实例代码给大家介绍go语言Timer计时器的用法,代码简单易懂,感兴趣的朋友跟随小编一起看看吧
    2020-05-05
  • 一文搞懂Golang中的内存逃逸

    一文搞懂Golang中的内存逃逸

    内存逃逸是 Go 语言中一个重要的概念,涉及到程序的性能优化和内存管理,了解内存逃逸可以帮助我们编写更高效的代码,本文将从基本概念入手,深入讲解 Go 语言中的内存逃逸现象,以及如何避免,需要的朋友可以参考下
    2023-12-12
  • Go语言并发之通知退出机制的实现

    Go语言并发之通知退出机制的实现

    本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-07-07
  • 一文详解Golang的中间件设计模式

    一文详解Golang的中间件设计模式

    最近在看一些rpc框架的使用原理和源码的时候,对中间件的实现非常感兴趣,所以这篇文章就来和大家聊聊Golang的中间件设计模式,希望对大家有所帮助
    2023-03-03
  • Go语言简易图像验证码生成的实战案例

    Go语言简易图像验证码生成的实战案例

    本篇我们将使用Go语言结合第三方库 github.com/mojocn/base64Captcha,快速实现一个简易图像验证码生成接口,具有一定的参考价值,感兴趣的可以了解一下
    2025-08-08
  • Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    Golang 实现 Redis系列(六)如何实现 pipeline 模式的 redis 客户端

    pipeline 模式的 redis 客户端需要有两个后台协程负责 tcp 通信,调用方通过 channel 向后台协程发送指令,并阻塞等待直到收到响应,本文是使用 golang 实现 redis 系列的第六篇, 将介绍如何实现一个 Pipeline 模式的 Redis 客户端。
    2021-07-07
  • Go映射的使用

    Go映射的使用

    Go提供了另一个重要的数据类型,称为map,它将唯一键映射到值,本文主要介绍了Go映射的使用,包括声明映射、初始化映射、操作映射等,感兴趣的可以了解一下
    2023-11-11
  • Go语言七篇入门教程四通道及Goroutine

    Go语言七篇入门教程四通道及Goroutine

    这篇文章主要为大家介绍了Go语言的通道及Goroutine示例详解,本文是Go语言七篇入门系列篇,有需要的朋友可以借鉴参考下,希望能够有所帮助
    2021-11-11

最新评论