我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Golang 手写一个简单的并发任务 manager

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Golang 手写一个简单的并发任务 manager

前言

今天也是偏实战的内容,作为一个并发复习课,很简单,我们来看看怎样实现一个并发任务 manager。

在微服务的场景下,我们有很多任务的执行是没有明确的先后顺序的,比如一个接口同时要做到任务 A 和 任务 B,两个任务分别拿到一些数据,最后组装裁剪后通过接口下发。

此时,A 和 B 两个任务没有依赖关系,如果我们串行来执行,会拖慢整个任务的执行节奏,用并发的方式来优化是一个方向。

那怎么实现呢?

errgroup

一个常见的想法是用 errgroup,我们之前也介绍过 Golang errgroup 设计和原理解析。

今天我们不打算用这种实现,希望用更加基础的组件来引发思考,看看如何活用 sync 包提供的基础能力。另外一点是 errgroup 也有他的缺陷,如果在启动的协程中没有手动 recover,那么一旦在我们的任务中出现 panic,整个程序就 crash 了。

这一点还是很有争议的,很多开发者认为这是符合预期的,也有一些开发者希望在 New 一个 errgroup 的时候能够提供 option 控制是否来 recover。近期还有两个 issue 在进行激烈的讨论,目前看没有定论。

感兴趣的同学可以看下这两个 issue:

  • x/sync/errgroup: why not recover the fn's err in errgroup #40484
  • proposal: x/sync/errgroup: propagate panics and Goexits through Wait #53757

需求拆解

ok,我们来试着用 sync 包基础能力来实现一个简单的并行任务 manager。首先我们分析下需求。

  • 一定要能做到并发执行各个任务,开多个协程,而不是在一个 main goroutine 里串行执行各个任务;
  • 并发安全,我们当然不希望出现数据异常,不希望并发执行任务导致最后程序因为 runtime error 而挂掉;
  • 如果多个任务都失败,只返回一个 error 即可;
  • 能够 recover from panic,不需要开发者使用的时候再手动去写 recover 逻辑;
  • 性能有保障。

并发执行这一点我们可以借助 sync.WaitGroup 的能力,每次启动一个goroutine,WaitGroup 就加 1,在 defer 里完成 Done,启动所有 goroutine 之后,等着 Wait 返回结果即可。常规的能力复用。

需要额外处理的地方在于,怎么实现多个线程只有一个 error 能赋值,以及 recover 的适配。

实战代码

我们理一下思路,看看代码怎么写。

Job

首先一定需要定义一个通用的函数签名,使得开发者能够传入自己要执行的并发任务。

type Job interface {
	Do(ctx context.Context, param interface{}) error
	Name() string
}

JobManager

我们的 job manager 现阶段可以简单实现,只是一组 Job 的集合:

type JobManager []Job

错误处理

要达到只有一个 error 赋值,且不出现 race condition,有两个方案:

  • sync.Mutex 加锁;
  • sync.Once 只执行一次。

当然,什么时候我们都可以用一把大锁解决问题,但它的性能不会很好,能用原子操作解决的尽量还是不要用 Mutex,这里参照 errgroup,我们可以用一个 Once 对象来控制只赋值一次。

panic 恢复可以直接在 defer 里面 recover 即可,需要能带出来 stack trace,把它变成一个 error 赋值

及时退出

有时候我们这个并发任务数量非常多,可能还没创建完 goroutine,某个先创建的任务就已经挂了,这时候需要有一个全局的信号,终止后续的 goroutine 创建。这一点用原子操作就能实现。

完整代码

把上面的分析落地,这样我们就实现了一个带上了 recover 能力,以及终止能力的的 errgroup。

package main
import (
	"context"
	"errors"
	"fmt"
	"sync"
	"sync/atomic"
)
type Job interface {
	Do(ctx context.Context, param interface{}) error
	Name() string
}
type JobManager []Job

func (mgr JobManager) Execute(ctx context.Context, param interface{}) error {
	var (
		stop    int32 = 0
		err     error
		wg      sync.WaitGroup
		errOnce sync.Once
	)

	for _, job := range mgr {
		if atomic.LoadInt32(&stop) > 0 {
			break
		}

		wg.Add(1)
		go func(j Job) {
			defer func() {
				wg.Done()
				if r := recover(); r != nil {
					errMsg := fmt.Sprintf("JobManager panic: job: %v, reason: %v", j.Name(), r)
					nerr := errors.New(errMsg)
					errOnce.Do(func() {
						if err == nil {
							err = nerr
						}
					})
					atomic.AddInt32(&stop, 1)
				}
			}()
			nerr := j.Do(ctx, param)
			if nerr != nil {
				atomic.AddInt32(&stop, 1)
				errOnce.Do(func() {
					if err == nil {
						err = nerr
					}
				})
			}
		}(job)
	}
	wg.Wait()
	return err
}

使用方法也很简单:

var mgr = JobManager{
	AJob, BJob, CJob, // 这里的各个 Job 需要实现一开始我们定义的接口
}
err := mgr.Execute(ctx, param)

这里我们需要定义统一的 param interface{},建议是一个接口,各个 Job 执行完毕后如果有需要写入的数据,可以调用 param 的 Setter 方法写入,最后直接拿 param 来做后续逻辑。

小结

今天我们用 sync.Once,以及 sync.WaitGroup 的能力实现了一个简易的并发任务调度器,希望能够帮助大家回顾一下此前介绍的并发相关概念和用法。其实并发管理这一点很多时候我们会存在依赖,这时候可能需要将多个 job 分层,或者梳理出来拓扑关系来执行,我们今天只是简单入门,复习一下相关知识。

建议大家回顾一下此前对于 once 以及 errgroup 的源码解析,相信你会更能融会贯通。

  • Golang errgroup 设计和原理解析
  • 解析 Golang sync.Once 用法和原理

到此这篇关于Golang 手写一个简单的并发任务 manager的文章就介绍到这了,更多相关Golang manager内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Golang 手写一个简单的并发任务 manager

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

一个简单的Go模拟——并发问题

php小编新一为大家带来了一个简单却有趣的Go模拟游戏,名为“并发问题”。这款游戏以并发编程为主题,让玩家在虚拟的世界中体验并发编程的魅力。游戏中,玩家需要通过编写代码来处理多个任务的同时执行,测试自己的并发编程能力。游戏界面简洁明了,操作
一个简单的Go模拟——并发问题
2024-02-09

TypeScript手写一个简单的eslint插件实例

这篇文章主要为大家介绍了TypeScript手写一个简单的eslint插件实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-02-06

如何利用MySQL和JavaScript开发一个简单的任务管理器

如何利用MySQL和JavaScript开发一个简单的任务管理器概述:任务管理器是一种常见的应用程序,它可以帮助我们组织和跟踪日常任务的完成情况。在本文中,我们将学习如何利用MySQL和JavaScript来开发一个简单的任务管理器。该管理
2023-10-22

基于Python编写一个简单的服务注册发现服务器

我们都知道有很多的非常著名的注册服务器,例如: Consul、ZooKeeper、etcd,甚至借助于redis完成服务注册发现。但是本篇文章我们将使用python socket写一个非常简单的服务注册发现服务器,感兴趣的可以了解一下
2023-05-16

基于C++编写一个简单的服务器

这篇文章主要为大家详细介绍了如何基于C++编写一个简单的服务器,文中的示例代码讲解详细,具有一定的参考价值,感兴趣的小伙伴可以了解一下
2023-03-14

基于Python编写一个简单的http服务器

这篇文章主要为大家详细介绍了如何基于Python编写一个简单的http服务器,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
2023-05-17

Go语言怎么实现一个简单的并发聊天室

今天小编给大家分享一下Go语言怎么实现一个简单的并发聊天室的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。并发聊天服务器这里主
2023-06-29

基于C++怎么编写一个简单的服务器

这篇文章主要讲解了“基于C++怎么编写一个简单的服务器”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“基于C++怎么编写一个简单的服务器”吧!先写个简易的controller基类继承反射基类,
2023-07-05

如何利用MySQL和Go语言开发一个简单的任务调度系统

下面是一个简单的示例,展示如何使用MySQL和Go语言开发一个任务调度系统:1. 安装MySQL数据库和Go语言开发环境。2. 创建一个MySQL数据库,用于存储任务和调度信息。可以使用以下命令创建一个名为task_scheduler的数据
2023-10-20

如何使用C++编写一个简单的电子商务平台?

如何使用C++编写一个简单的电子商务平台?随着互联网的发展和普及,电子商务已经成为了现代商业运营的重要手段。对于开发者来说,了解如何使用C++编写一个简单的电子商务平台是非常有用的技能。本文将介绍一些基本的概念和技术,以帮助你快速上手。首先
如何使用C++编写一个简单的电子商务平台?
2023-11-02

如何使用MySQL和Ruby实现一个简单的任务调度功能

要使用MySQL和Ruby实现一个简单的任务调度功能,可以按照以下步骤操作:1. 安装并配置MySQL数据库:首先,确保你的系统中已经安装了MySQL数据库,并且创建了一个用于存储任务信息的数据库。然后,创建一个任务表,包含任务的名称、描述
2023-10-20

如何使用MongoDB开发一个简单的电子商务网站

如何使用MongoDB开发一个简单的电子商务网站作为一种流行的非关系型数据库,MongoDB在电子商务网站的开发中具有很大的优势。它的可伸缩性和灵活性使得它成为构建强大而且易于扩展的电子商务网站的理想选择。本文将向您介绍如何使用MongoD
2023-10-22

如何使用MySQL和Ruby实现一个简单的异步任务调度功能

如何使用MySQL和Ruby实现一个简单的异步任务调度功能以前的Web应用程序大多采用同步的方式来处理请求,即用户发送请求后,服务器会立即处理完请求并返回结果。然而,随着应用程序复杂度的增加,同步方式的处理效率逐渐变得低下,因此异步任务调度
2023-10-22

如何用Go语言开发一个简单的电子商务平台

如何用Go语言开发一个简单的电子商务平台一、引言近年来,随着互联网的迅猛发展,电子商务平台成为了现代商业发展的重要组成部分。而在开发电子商务平台的过程中,选择一门合适的编程语言显得尤为重要。Go语言因其高效、协程、并发等特点,成为了开发电子
如何用Go语言开发一个简单的电子商务平台
2023-11-20

如何利用MySQL和Ruby开发一个简单的电子商务网站

要利用MySQL和Ruby开发一个简单的电子商务网站,你可以按照以下步骤进行:1. 安装MySQL数据库和Ruby开发环境:首先,在你的计算机上安装MySQL数据库和Ruby开发环境。你可以从官方网站下载并按照说明进行安装。2. 创建数据库
2023-10-10

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录