编程 一个小例子,给你讲透典型的 Go 并发操作

2024-11-18 06:20:43 +0800 CST views 520

一个小例子,给你讲透典型的 Go 并发操作

在 Go 中,如果任务可以分解为多个子任务并发执行,等到所有子任务执行完毕后再进行下一步处理,使用 sync.WaitGroup 是非常合适的选择。然而,尽管 sync.WaitGroup 用法简单,仍然存在一些容易踩到的坑。本文将通过一个小例子详细介绍 sync.WaitGroup 的正确使用及常见的陷阱。

sync.WaitGroup 的正确使用

我们有一个任务需要执行 3 个子任务,可以这样使用 sync.WaitGroup

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup

    wg.Add(3)

    go handlerTask1(&wg)
    go handlerTask2(&wg)
    go handlerTask3(&wg)

    wg.Wait()  // 等待所有子任务执行完毕

    fmt.Println("全部任务执行完毕.")
}

func handlerTask1(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("执行任务 1")
}

func handlerTask2(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("执行任务 2")
}

func handlerTask3(wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println("执行任务 3")
}

输出结果:

执行任务 3
执行任务 1
执行任务 2
全部任务执行完毕.

sync.WaitGroup 闭坑指南

1. 传递指针

正确做法:传递 sync.WaitGroup 的指针。

go handlerTask1(&wg)  // 正确

错误做法:传递 sync.WaitGroup 的值。

go handlerTask1(wg)   // 错误

2. wg.Add() 位置

错误示例:将 wg.Add() 放在 go handlerTask() 中。

var wg sync.WaitGroup
go handlerTask1(&wg)
wg.Wait()

func handlerTask1(wg *sync.WaitGroup) {
    wg.Add(1)   // 错误位置
    defer wg.Done()
    fmt.Println("执行任务 1")
}

wg.Wait() 之前,必须先调用 wg.Add()。因此,wg.Add() 应在启动 goroutine 之前调用。

sync.WaitGroup + Context

当需求是在某个子任务失败时取消其他任务的执行,sync.WaitGroup 本身无法实现此功能,此时可以结合 context 来处理。

代码示例:

package main

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

// handlerTask 处理单个任务
func handlerTask(ctx context.Context, taskId int, wg *sync.WaitGroup, cancel context.CancelFunc) {
    defer wg.Done()

    fmt.Printf("Request %d is processing...\n", taskId)

    // 模拟请求处理,如果 taskId 为1,则模拟失败
    if taskId == 1 {
        fmt.Printf("Request %d failed\n", taskId)
        cancel()  // 取消 context,通知其他请求停止
        return
    }

    // 监听 context.Done() 通道
    select {
    case <-ctx.Done():
        fmt.Printf("Request %d is already cancelled\n", taskId)
        return
    default:
        time.Sleep(3 * time.Second)  // 模拟耗时操作
        fmt.Printf("Request %d succeeded\n", taskId)
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    var wg sync.WaitGroup

    wg.Add(3)
    go handlerTask(ctx, 1, &wg, cancel)  // 模拟失败任务
    go handlerTask(ctx, 2, &wg, cancel)  // 模拟成功任务
    go handlerTask(ctx, 3, &wg, cancel)  // 模拟成功任务

    // 等待所有任务完成或被取消
    go func() {
        wg.Wait()
        fmt.Println("All requests are finished or cancelled")
    }()

    time.Sleep(5 * time.Second)  // 给一些时间处理,防止主 goroutine 过早退出
}

执行结果:

Request 1 is processing...
Request 1 failed
Request 2 is processing...
Request 3 is processing...
Request 2 is already cancelled
Request 3 is already cancelled
All requests are finished or cancelled

解释:

  1. 任务 1 失败

    • 任务 1 处理开始,发现任务 ID 是 1,输出 "Request 1 failed" 并调用 cancel(),取消 context,通知其他任务停止执行。
  2. 任务 2 和任务 3 被取消

    • 任务 2 和任务 3 监听 ctx.Done() 通道,发现通道已关闭,输出 "Request 2 is already cancelled" 和 "Request 3 is already cancelled"。
  3. 等待所有任务结束

    • 使用 wg.Wait() 等待所有任务完成或取消,输出 "All requests are finished or cancelled"。

总结

通过这两个例子,你可以看到:

  • sync.WaitGroup 适用于等待所有子任务执行完毕后再进行下一步处理,使用时注意传递指针并保证 wg.Add()wg.Done() 的调用次数一致。
  • sync.WaitGroup + context 组合适用于当某个任务失败时,及时取消其他任务的执行,确保资源的有效利用。

通过合理使用 sync.WaitGroupcontext,可以简化并发编程中的许多场景,让代码更加健壮。

复制全文 生成海报 Go语言 并发编程 编程技巧

推荐文章

使用Python实现邮件自动化
2024-11-18 20:18:14 +0800 CST
微信小程序热更新
2024-11-18 15:08:49 +0800 CST
10个极其有用的前端库
2024-11-19 09:41:20 +0800 CST
如何开发易支付插件功能
2024-11-19 08:36:25 +0800 CST
使用 node-ssh 实现自动化部署
2024-11-18 20:06:21 +0800 CST
Node.js中接入微信支付
2024-11-19 06:28:31 +0800 CST
Vue3中如何实现插件?
2024-11-18 04:27:04 +0800 CST
一个有趣的进度条
2024-11-19 09:56:04 +0800 CST
使用Vue 3和Axios进行API数据交互
2024-11-18 22:31:21 +0800 CST
PHP如何进行MySQL数据备份?
2024-11-18 20:40:25 +0800 CST
go错误处理
2024-11-18 18:17:38 +0800 CST
Redis和Memcached有什么区别?
2024-11-18 17:57:13 +0800 CST
Golang 随机公平库 satmihir/fair
2024-11-19 03:28:37 +0800 CST
#免密码登录服务器
2024-11-19 04:29:52 +0800 CST
Go 接口:从入门到精通
2024-11-18 07:10:00 +0800 CST
Vue3 组件间通信的多种方式
2024-11-19 02:57:47 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
程序员茄子在线接单