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

news/2024/12/21 22:40:42/文章来源:https://www.cnblogs.com/cheyunhua/p/18403194

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

原创 訢亮 程序员新亮
 
程序员新亮
GitHub 9K+ Star | 技术交流分享
206篇原创内容

如果你有一个任务可以分解成多个子任务进行处理,同时每个子任务没有先后执行顺序的限制,等到全部子任务执行完毕后,再进行下一步处理。这时每个子任务的执行可以并发处理,这种情景下适合使用 sync.WaitGroup

虽然 sync.WaitGroup 使用起来比较简单,但是一不留神很有可能踩到坑里。

sync.WaitGroup 正确使用

比如,有一个任务需要执行 3 个子任务,那么可以这样写:

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 闭坑指南

01

// 正确
go handlerTask1(&wg)

// 错误
go handlerTask1(wg)

执行子任务时,使用的 sync.WaitGroup 一定要是 wg 的引用类型!

02

注意不要将 wg.Add() 放在 go handlerTask1(&wg) 中!

例如:

// 错误
var wg sync.WaitGroup

go handlerTask1(&wg)

wg.Wait()

...

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

注意 wg.Add() 一定要在 wg.Wait() 执行前执行!

小结

注意 wg.Add() 和 wg.Done() 的计数器保持一致!其实 wg.Done() 就是执行的 wg.Add(-1) 。

其实 sync.WaitGroup 使用场景比较局限,仅适用于等待全部子任务执行完毕后,再进行下一步处理。如果需求是当第一个子任务执行失败时,通知其他子任务停止运行,这时 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")
 }()

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

解释

1.任务 1 开始处理:

  • 输出 Request 1 is processing...。
  • 检查任务 ID,发现是 1,输出 Request 1 failed。
  • 调用 cancel(),取消上下文 ctx。

2.任务 2 和任务 3 开始处理:

  • 输出 Request 2 is processing... 和 Request 3 is processing...。
  • 由于任务 1 已经调用 cancel(),上下文 ctx 已经被取消。
  • 进入 select 语句,检查 ctx.Done() 通道。
  • 发现 ctx.Done() 通道已被关闭,输出 Request 2 is already cancelled 和 Request 3 is already cancelled。

3.等待所有任务完成或被取消:

  • 等待所有任务完成或被取消。
  • 输出 All requests are finished or cancelled。

通过这种方式,可以确保在任务 1 失败时,其他任务能够立即检测到取消信号并停止执行。

图片

图片

图片

程序员新亮
GitHub 9K+ Star | 技术交流分享
206篇原创内容
阅读 72
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/794108.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 刷题—树

不会做简单题目的小菜菜!一:树 1、树描述的是一个父子关系;有节点;根节点;叶子节点三个相关的概念 2、树的高度;深度;层 3、二叉树:每个节点最多只有两个孩子 4、完全二叉树:除了叶子节点;每个孩子并不要求都为两个孩子(从上到下,从左到右依次填满节点) 5、满二叉…

软件安装——STM32开发

安装Keil5 MDK 安装器件支持包 软件注册 安装STLINK驱动 安装USB转串口驱动安装Keil5 MDK 下载链接(来自B站江协科技) 通过百度网盘分享的文件:STM32入门教程资料 链接:https://pan.baidu.com/s/17_sbwMXU-xBVFA2sK0smrg?pwd=cgn6 提取码:cgn6 解压并安装Keil5MDK安装包中…

findbi测试连接失败

导入的jar包没问题,然后装一个隔离包如下名字。因为没重启

A Fast and High Quality Multilevel Scheme for Partitioning Irregular Graphs

目录概METISCoarseningPartitioning phaseUncoarsening phaseKarypis G. and Kumar V. A fast and high quality multilevel scheme for partitioning irregular graphs. SIAM, 1998.概 本文提出了一种 multilevel graph partitioning 方法. METISMETIS 的思想比较简单:首先对原…

小猪佩奇学英语——第五天——hide and seek

例句hide and seek,捉迷藏seek,寻找 find,找到了It is Georges turn to hide.Its our turn.用 is 是因为虽然我们是2个以上的人,但是我们是一组,轮到我们的只有一轮,所以是单数。 Its your turn.轮到你们了 Its theri turn.轮到他们了。Its sbs turn to .... 轮到某人做某…

小猪佩奇学英语——第六天——The Playgroup

第六天——The Playgroup 例句George,are you looking forward to the playgroup?乔治,你现在是不是正期待去托儿所。 look forward to sth 期待某物 look forward to doing...期待做某事 用looking表示正期待....maybe George is too small to go to my playgroup?too +形容…

实现人形角色的攀爬

在Unity实现角色攀爬 前言 开放世界类型的游戏近年也热门起来了,自由攀爬也成了这一类游戏的一大特色。攀爬给了玩家更多探索路径的选择,也让地图设计有了更多思路。这次,我们就来尝试在Unity中制作一个人形角色的攀爬。注:攀爬是一个角色完整动作系统的一部分,本文暂且抛…

LVDS(FPGA)

差分输入时钟缓冲器(IBUFDS)点击查看代码// IBUFDS: Differential Input Buffer// 7 Series// Xilinx HDL Language Template, version 2024.1IBUFDS #(.DIFF_TERM("FALSE"), // Differential Termination.IBUF_LOW_PWR("TRUE"), // L…

51nod 石子分配

可以发现步数限制把数轴变为了环。环之间不可以交换,环内相邻两点可以交换,然后我们只需要对每个环操作,最后累加。 对于环上的每个石子堆,我们需要将其石子数调整到均值 \(avg\)。因此,我们首先计算每个堆石子相对于 \(avg\) 的偏差,即 \(nowa[i] - avg\)。 因为相邻节点…

马哥教育C10网络安全课第四周作业2024_0831

网络安全C10-2024.8.31 作业: 1、安装burp并实现抓取HTTP站点的数据包(HTTPS站点暂时不要求) (1)安装Java 1.8.144; 设定操作系统环境变量 - JAVA_HOME jdk文件夹的绝对路径 例: JAVA_HOME=C:\Program Files\Java\jdk1.8.0_144 - CLASSPATH CLASSJPATH=.;%JAVA_HOME%\lib…

十年来(2015-2024)植物/农学领域有哪些学者戴上了杰青帽子?

2024年杰青建****议资助项目申请人名单(部分名单)(注:该表只是统计现公布的名单,欢迎留言补充) 2023年杰青建议资助项目申请人名单2022年杰青建议资助项目申请人名单2020年杰青建议资助项目申请人名单2019杰青建议资助项目申请人名单2018杰青建议资助项目申请人名单2017杰…