首页 > 其他分享 >一个小例子,给你讲透典型的 Go 并发操作

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

时间:2024-09-08 18:02:25浏览次数:8  
标签:wg WaitGroup fmt sync 并发 任务 例子 go Go

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

原创 訢亮 程序员新亮    2024年09月08日 16:57 天津 听全文 程序员新亮 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   ​

标签:wg,WaitGroup,fmt,sync,并发,任务,例子,go,Go
From: https://www.cnblogs.com/cheyunhua/p/18403194

相关文章

  • 利用Django框架快速构建Web应用:从零到上线
    随着互联网的发展,Web应用的需求日益增长,而Django作为一个高级的PythonWeb框架,以其强大的功能和灵活的架构,成为了众多开发者的选择。本文将指导你如何从零开始使用Django框架构建一个简单的Web应用,并将其部署到线上,让世界看到你的作品。Django简介Django是由AdrianHolov......
  • ginkgo编写测试用例
    gogetgithub.com/onsi/ginkgo/v2/ginkgogoinstallgithub.com/onsi/ginkgo/v2/ginkgogogetgithub.com/onsi/gomegamkdirtestcdtestginkgobootstrapginkgo常用的模块是It、Describe、BeforeEach、AfterEach、BeforeSuite、AfterSuite。It指定单个测试用例。Describe......
  • Django
     web框架,python开发设计模式:MTV模式Model模型,数据库操作Template模板,前端页面View视图,处理业务逻辑函数pipinstalldjango==3.2.24importdjangodjango.get_version()启动django项目(http://127.0.0.1:8000):python./manage.pyrunserver settings.py (ALLOWED_HOST=......
  • 基于django+vue重点实验室通用官方网站设计与实现【开题报告+程序+论文】-计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着科技创新的不断推进,重点实验室作为科研创新体系的核心组成部分,承载着推动学科发展、培养高层次人才、开展前沿技术研究与转化等重要使......
  • 基于django+vue中医学习系统【开题报告+程序+论文】-计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景在全球化与健康意识日益增强的今天,中医作为中华民族的传统医学瑰宝,其独特的理论体系和治疗方法正逐渐受到国际社会的广泛关注和认可。然而......
  • 基于django+vue中医问诊信息管理系统【开题报告+程序+论文】-计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展和中医诊疗模式的现代化转型,传统中医问诊方式面临着效率与标准化管理的双重挑战。中医问诊作为中医诊疗的核心环节......
  • 基于django+vue中医文化展示管理系统【开题报告+程序+论文】-计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景在全球化与文化多元化的今天,中医文化作为中华民族宝贵的非物质文化遗产,其独特的理论体系、丰富的诊疗方法以及深厚的文化底蕴,正逐渐受到国......
  • 并发编程数据结构-栈
    并发编程数据结构-栈有锁栈Stack1-基础线程安全栈Stack1是一个简单的线程安全栈实现,使用了std::mutex来保证push和pop操作的原子性。主要特点包括:使用std::lock_guard确保操作期间栈的线程安全。提供了两种push操作(左值引用和右值引用),优化了性能。pop操作抛......
  • 【爬虫开发】爬虫开发从0到1全知识教程第10篇:Mongodb数据库,介绍【附代码文档】
    本教程的知识点为:爬虫概要爬虫基础爬虫概述知识点:1.爬虫的概念requests模块requests模块知识点:1.requests模块介绍1.1requests模块的作用:数据提取概要数据提取概述知识点1.响应内容的分类知识点:了解响应内容的分类Selenium概要selenium的介绍知识点:1.sele......
  • 基于django+vue中小型制造型企业erp管理系统【开题报告+程序+论文】-计算机毕设
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着信息技术的飞速发展,企业信息化管理已成为提升企业竞争力、优化资源配置、提高运营效率的重要手段。中小型制造型企业作为国民经济的重......