在 Go 语言并发编程中,合理的并发模式能显著提升程序的可维护性和性能。本文将深入解析三种典型的并发模式实现,通过具体案例展示如何优雅地管理任务生命周期、资源池和工作 goroutine 池。

一、runner 模式:任务生命周期管理

在定时任务、批处理等场景中,我们需要对任务执行时间进行控制,并在收到中断信号时安全终止任务。runner 模式通过通道和超时机制实现了这一需求。

1. 核心实现原理

runner 模式的核心在于通过三个通道协同管理任务状态:

  • interrupt通道接收操作系统中断信号
  • complete通道报告任务完成状态
  • timeout通道控制任务执行超时

下面是 runner 包的核心实现:

// Runner 管理任务执行生命周期
type Runner struct {interrupt chan os.Signal    // 接收中断信号complete  chan error       // 任务完成通知timeout   <-chan time.Time // 超时控制tasks     []func(int)      // 任务列表closed    bool             // 运行状态
}// New 创建新的Runner实例
func New(d time.Duration) *Runner {return &Runner{interrupt: make(chan os.Signal, 1),complete:  make(chan error),timeout:   time.After(d),}
}// Add 添加任务到Runner
func (r *Runner) Add(tasks ...func(int)) {r.tasks = append(r.tasks, tasks...)
}// Start 启动任务执行并监视状态
func (r *Runner) Start() error {// 注册中断信号处理signal.Notify(r.interrupt, os.Interrupt)// 启动任务执行goroutinego func() {r.complete <- r.run()}()// 等待任务完成或超时select {case err := <-r.complete:return errcase <-r.timeout:return errors.New("任务执行超时")}
}// run 按顺序执行注册的任务
func (r *Runner) run() error {for id, task := range r.tasks {// 检查是否收到中断信号if r.gotInterrupt() {return errors.New("收到中断信号")}// 执行任务task(id)}return nil
}// gotInterrupt 检测中断信号
func (r *Runner) gotInterrupt() bool {select {case <-r.interrupt:signal.Stop(r.interrupt)return truedefault:return false}
}

2. 应用场景示例

以下是使用 runner 模式实现定时任务的案例,任务将在 3 秒内执行,超时或收到中断时终止:

func main() {log.Println("开始执行任务...")// 创建3秒超时的Runnerr := runner.New(3 * time.Second)// 添加三个任务r.Add(func(id int) {log.Printf("任务 %d 执行中...", id)time.Sleep(1 * time.Second)},func(id int) {log.Printf("任务 %d 执行中...", id)time.Sleep(2 * time.Second)},func(id int) {log.Printf("任务 %d 执行中...", id)time.Sleep(3 * time.Second)},)// 执行任务并处理结果if err := r.Start(); err != nil {switch err {case errors.New("任务执行超时"):log.Println("任务超时,终止执行")case errors.New("收到中断信号"):log.Println("收到中断,终止执行")}}log.Println("任务处理完成")
}

3. 关键特性解析

  • 超时控制:通过time.After设置任务整体执行超时时间
  • 中断处理:利用signal.Notify捕获系统中断信号
  • 任务顺序执行:按添加顺序依次执行任务,适合有依赖关系的场景
  • 优雅退出:无论超时还是中断,都能确保资源释放

二、pool 模式:资源池管理

在数据库连接、文件句柄等资源管理场景中,资源池模式能有效复用资源,避免频繁创建和销毁带来的性能损耗。

1. 资源池核心设计

pool 模式通过有缓冲通道实现资源的获取与释放,确保资源复用:

// Pool 管理可复用资源池
type Pool struct {m        sync.Mutex          // 互斥锁保护资源池resources chan io.Closer     // 资源通道factory  func() (io.Closer, error) // 资源创建工厂closed   bool                // 资源池状态
}// New 创建新的资源池
func New(fn func() (io.Closer, error), size uint) (*Pool, error) {if size <= 0 {return nil, errors.New("资源池大小不能小于1")}return &Pool{factory:   fn,resources: make(chan io.Closer, size),}, nil
}// Acquire 从资源池获取资源
func (p *Pool) Acquire() (io.Closer, error) {select {// 有空闲资源时直接获取case r, ok := <-p.resources:if !ok {return nil, errors.New("资源池已关闭")}return r, nil// 无空闲资源时创建新资源default:return p.factory()}
}// Release 释放资源回池
func (p *Pool) Release(r io.Closer) {p.m.Lock()defer p.m.Unlock()// 池已关闭时直接关闭资源if p.closed {r.Close()return}// 尝试将资源放回池,满时关闭资源select {case p.resources <- r:log.Println("资源放回池")default:log.Println("资源池已满,关闭资源")r.Close()}
}// Close 关闭资源池并释放所有资源
func (p *Pool) Close() {p.m.Lock()defer p.m.Unlock()if p.closed {return}p.closed = true// 关闭通道并释放资源close(p.resources)for r := range p.resources {r.Close()}
}

2. 数据库连接池应用案例

以下是使用 pool 模式管理数据库连接的示例,模拟创建和复用数据库连接:

// dbConnection 模拟数据库连接
type dbConnection struct {ID int32
}// Close 实现io.Closer接口
func (db *dbConnection) Close() error {log.Printf("关闭连接 %d\n", db.ID)return nil
}var idCounter int32// createConnection 连接创建工厂
func createConnection() (io.Closer, error) {id := atomic.AddInt32(&idCounter, 1)log.Printf("创建新连接 %d\n", id)return &dbConnection{ID: id}, nil
}func main() {// 创建包含2个连接的资源池p, err := pool.New(createConnection, 2)if err != nil {log.Fatal(err)}defer p.Close()var wg sync.WaitGroupwg.Add(5) // 5个任务竞争2个连接// 模拟5个任务获取连接for i := 0; i < 5; i++ {go func(taskID int) {defer wg.Done()// 获取连接conn, err := p.Acquire()if err != nil {log.Fatal(err)}defer p.Release(conn)// 模拟数据库操作log.Printf("任务 %d 使用连接 %d\n", taskID, conn.(*dbConnection).ID)time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)}(i)}wg.Wait()log.Println("所有任务完成")
}

3. 资源池设计要点

  • 接口抽象:通过io.Closer接口实现资源统一管理
  • 动态扩容:无空闲资源时自动创建新资源
  • 安全释放:通过互斥锁保证并发安全
  • 优雅关闭:关闭时释放所有资源,避免泄漏

三、work 模式:goroutine 池实现

在需要控制并发量的场景中,work 模式通过固定数量的 goroutine 池处理任务,避免创建过多 goroutine 导致资源耗尽。

1. 工作池核心实现

work 模式通过无缓冲通道实现任务与工作 goroutine 的同步:

// Worker 定义工作接口
type Worker interface {Task()
}// Pool 工作goroutine池
type Pool struct {work chan Worker  // 任务通道wg   sync.WaitGroup // 等待组
}// New 创建新的工作池
func New(maxGoroutines int) *Pool {p := Pool{work: make(chan Worker),}p.wg.Add(maxGoroutines)for i := 0; i < maxGoroutines; i++ {go func() {// 从通道获取任务并执行for w := range p.work {w.Task()}p.wg.Done()}()}return &p
}// Run 提交任务到工作池
func (p *Pool) Run(w Worker) {p.work <- w
}// Shutdown 关闭工作池
func (p *Pool) Shutdown() {close(p.work)p.wg.Wait()
}

2. 任务处理应用案例

以下是使用 work 模式处理批量任务的示例,限制同时运行 3 个 goroutine:

// task 实现Worker接口
type task struct {id int
}func (t task) Task() {log.Printf("任务 %d 开始处理\n", t.id)// 模拟任务处理时间time.Sleep(time.Duration(rand.Intn(2000)) * time.Millisecond)log.Printf("任务 %d 处理完成\n", t.id)
}func main() {// 创建包含3个工作goroutine的池p := work.New(3)defer p.Shutdown()var wg sync.WaitGroupwg.Add(10) // 10个任务// 提交10个任务for i := 0; i < 10; i++ {go func(id int) {defer wg.Done()p.Run(task{id: id})}(i)}wg.Wait()log.Println("所有任务处理完毕")
}

3. 工作池特性分析

  • 固定并发量:通过控制 goroutine 数量避免系统负载过高
  • 任务同步:无缓冲通道保证任务与工作 goroutine 一一对应
  • 简洁易用:通过接口抽象任务逻辑,解耦业务与并发控制
  • 优雅退出:Shutdown 方法确保所有任务完成后退出

四、三种模式的应用场景对比

模式核心特性适用场景典型案例
runner任务超时控制与中断处理定时任务、批处理作业数据备份、定时报表生成
pool资源复用与管理数据库连接、文件句柄等资源管理高并发 Web 服务连接池
work固定并发量任务处理批量任务处理、限制并发请求图片处理、日志分析

五、并发模式最佳实践

  1. 根据场景选择模式

    • 需要超时控制时优先使用 runner 模式
    • 资源复用场景选择 pool 模式
    • 限制并发量场景使用 work 模式
  2. 接口抽象原则
    通过接口解耦业务逻辑与并发控制,如 runner 的任务函数、pool 的资源接口、work 的 Task 方法

  3. 资源释放策略
    所有模式都应实现优雅关闭机制,确保资源正确释放,避免泄漏

  4. 监控与调优
    在生产环境中添加监控指标,根据负载调整参数,如 pool 的大小、work 的 goroutine 数量

Go 语言的并发模式通过简洁的设计解决了复杂的并发控制问题,合理应用这些模式能让代码更清晰、更健壮,同时提升系统的性能和稳定性。在实际开发中,可根据具体需求组合或扩展这些模式,打造更适合业务场景的并发解决方案。

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

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

相关文章

【Java 开发日记】你会不会使用 SpringBoot 整合 Flowable 快速实现工作流呢?

目录 1、流程引擎介绍 2、创建项目 3、画流程图 4、开发接口 4.1 Java 类梳理 ProcessDefinition ProcessInstance Activity Execution Task 4.2 查看流程图 4.3 开启一个流程 4.4 将请求提交给组长 4.5 组长审批 4.6 经理审批 4.7 拒绝流程 1、流程引擎介绍 …

面试150 分发糖果

思路 联想贪心算法&#xff0c;遍历两次数组&#xff0c;一次是从左到右遍历&#xff0c;只比较右边孩子评分比左边打的情况。第二次从右到左遍历&#xff0c;只比较左边孩子评分比右边大的情况。最后求和即可 class Solution:def candy(self, ratings: List[int]) -> int…

csp基础之进制转换器

一、进制转换要明白的基础知识。。。 1、什么是进制&#xff1f; 进制也就是进位计数制&#xff0c;是人为定义的带进位的计数方法。对于任何一种进制 X 进制&#xff0c;就表示每一位置上的数运算时都是逢 X 进一位。十进制是逢十进一&#xff0c;十六进制是逢十六进一&#…

Zephyr OS蓝牙广播(Advertising)功能实现

目录 概述 1 Advertising功能介绍 1.1 实现原理 1.2 广播类型 1.3 广播数据格式 1.4 优化建议 1.5 常见问题和解决方法 2 Nordic 蓝牙广播&#xff08;Advertising&#xff09;功能实现 2.1 环境准备与SDK基础 2.2 广播功能实现 2.3 广播优化与最佳实践 3 实际应用案例…

服务器不支持PUT,DELETE 的解决方案

nginx 的更改&#xff1a; set $method $request_method; if ($http_X_HTTP_Method_Override ~* PUT|DELETE) { set $method $http_X_HTTP_Method_Override; } proxy_method $method; axios 的更改&#xff1a; const method config.me…

从0开始学习计算机视觉--Day04--线性分类

从宏观来看&#xff0c;卷积网络可以看做是由一个个不同的神经网络组件组合而成&#xff0c;就像积木一样通过不同类型的组件搭建形成&#xff0c;其中线性分类器是一个很重要的组件&#xff0c;在很多卷积网络中都有用到&#xff0c;所以了解清楚它的工作原理对我们后续的学习…

基于ComfyUI与Wan2.1模型的本地化视频生成环境搭建指南

文章目录 前言1.软件准备1.1 ComfyUI1.2 文本编码器1.3 VAE1.4 视频生成模型2.整合配置3. 本地运行测试4. 公网使用Wan2.1模型生成视频4.1 创建远程连接公网地址5. 固定远程访问公网地址总结前言 各位小伙伴们,今天我们将为您展示一套创新的人工智能应用方案!本次教程将指导…

Vue 2 项目中内嵌 md 文件

推荐方案&#xff1a;raw-loader marked 解析 Markdown 第一步&#xff1a;安装依赖 npm install marked --save npm install raw-loader --save-dev第二步&#xff1a;配置 webpack 支持 .md 文件 打开 vue.config.js 或 webpack.config.js&#xff0c;添加以下配置&#…

Spring AI初识及简单使用,快速上手。

Spring AI简介 在当今这样一个快速发展的技术时代&#xff0c;人工智能&#xff08;AI&#xff09;已经成为各行各业的一种标配。而作为一款主流的Java应用开发框架Spring&#xff0c;肯定会紧跟时代的潮流&#xff0c;所以&#xff0c;推出了Spring AI框架。 官网描述&#…

Flask中的render_template与make_response:生动解析与深度对比

文章目录 Flask中的render_template与make_response&#xff1a;生动解析与深度对比一、&#x1f31f; 核心概念速览二、&#xfffd; render_template - 网页内容的主厨特点与内部机制适用场景高级用法示例 三、&#x1f381; make_response - 响应的包装专家核心功能解析适用…

WordPress目录说明

在WordPress建站过程中&#xff0c;理解服务器目录结构是非常重要的。以下是一个基础的WordPress服务器目录指南&#xff1a; /wp-admin/ &#xff1a;这个目录包含了WordPress网站的所有管理功能&#xff0c;包括用于处理网站后台的所有PHP文件。 /wp-includes/ &#xff1a;…

HTTP面试题——缓存技术

目录 HTTP缓存技术有哪些&#xff1f; 什么是强制缓存&#xff1f; 什么是协商缓存&#xff1f; HTTP缓存技术有哪些&#xff1f; 对于一些具有重复性的HTTP请求&#xff0c;比如每次请求得到的数据都是一样的&#xff0c;我们可以把这对 请求-响应的数据都缓存在本地&#x…

virtual box 不能分配 USB设备 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虚拟电脑 win10

VirtualBox: Failed to attach the USB device to the virtual machine – Bytefreaks.net ISSUE&#xff1a; virtual box 不能分配 USB设备 IFX DAS JDS TriBoard TC2X5 V2.0 [0700] 到虚拟电脑 win10. USB device IFX DAS JDS TriBoard TC2X5 V2.0 with UUID {91680aeb-e1…

Deepoc大模型重构核工业智能基座:混合增强架构与安全增强决策技术​

面向复杂系统的高可靠AI赋能体系构建 Deepoc大模型通过多维度技术突破&#xff0c;显著提升核工业知识处理与决策可靠性。经核能行业验证&#xff0c;其生成内容可验证性提升68%&#xff0c;关键参数失真率<0.3%&#xff0c;形成覆盖核能全链条的定制化方案&#xff0c;使企…

第12章:冰箱里的CT扫描仪——计算机视觉如何洞穿食材的“生命密码“

第11章:冰箱里的CT扫描仪——计算机视觉如何成为食材健康的"超级诊断官" “糟了!冰箱里草莓长出了白色绒毛,鸡胸肉渗出了可疑的粉红色液体!” 这揭示了厨房生存的更基本挑战:如何像经验丰富的主厨一样,一眼洞穿食材的健康密码? 本章将揭示计算机视觉技术如何赋…

虚幻基础:窗口——重定向

能帮到你的话&#xff0c;就给个赞吧 &#x1f618; 文章目录 重定向&#xff1a;给骨架添加兼容骨架。使得不同模型间复用动画资源 重定向&#xff1a;给骨架添加兼容骨架。使得不同模型间复用动画资源

CSS 逐帧动画

CSS 逐帧动画实现指南 逐帧动画(frame-by-frame animation)是一种通过快速连续显示一系列静态图像来创造运动效果的技术。以下是使用CSS实现逐帧动画的几种方法。 1. 使用 steps() 计时函数 这是实现逐帧动画最常用的方法&#xff0c;通过animation-timing-function的steps(…

高版本IDEA如何开发低版本jdk项目

问题描述 我这个人比较喜欢新的东西&#xff0c;比如使用idea的时候&#xff0c;我就喜欢最新版本。 但是有个问题&#xff0c;最新版本的idea好像不支持jdk1.6&#xff0c;导致我无法去用新版本idea开发项目。 直到有一天&#xff0c;idea给了我一个提示如下&#xff0c;之…

Java设计模式->责任链模式的介绍

目录 1、责任链模式概念 1.1、定义介绍 1.2、流程图 1.3、优缺点 2、实现 3、应用场景 3.1、Springmvc流程 3.2、mybatis的执行流程 3.3、Spring的过滤器和拦截器 3.4、sentinel限流熔断 3.5、aop的加载和使用 4、举例 前言 是一种 行为型设计模式&#xff0c;它通…

【Bluedroid】蓝牙启动之 btm_acl_device_down 流程源码解析

本文详细分析Android蓝牙协议栈在设备故障时的处理流程。当蓝牙设备发生硬件故障或系统异常时,协议栈通过btm_acl_device_down触发多层次的资源清理和状态重置,包括ACL连接终止、L2CAP通道释放、SCO连接清理、BLE拓扑更新、设备数据库重置等关键操作,确保系统安全恢复。 一、…