golang开发:goroutine在项目中的使用姿势

2024-03-11 03:12

本文主要是介绍golang开发:goroutine在项目中的使用姿势,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

很多初级的Gopher在学习了goroutine之后,在项目中其实使用率不高,尤其一些跨语言过来的人,对并发编程理解不深入,可能很多人只知道go func(),或者掌控不够,谨慎一些,尽量少使用或者不使用,用的话就是go func(),主要列一下我们这边的主要使用方法。

goroutine在项目中的使用方法

看一下样例代码,实际上,我们生产环境中就是这么使用的。

package logicimport ("context""fmt""sync""time"
)type UserData struct {Age     intName    stringPostion string
}type ServerLogic struct {ctx    context.Contextcancel func()waiter sync.WaitGroupch     chan UserData
}func NewServerLogic(logCtx *context.Context, worker int, queue int) *ServerLogic {logic := &ServerLogic{}logic.InitWorker(worker, queue)return logic
}func (this *ServerLogic) InitWorker(workers int, queue int) {this.ch = make(chan UserData, queue)this.ctx, this.cancel = context.WithCancel(context.Background())this.waiter.Add(workers)for i := 0; i < workers; i++ {go this.Proc()}
}func (this *ServerLogic) Proc() {defer this.waiter.Done()for {select {case t := <-this.ch:this.Dothing(t)case <-this.ctx.Done():return}}
}func (this *ServerLogic) Dothing(data UserData) error {//do codetime.Sleep(time.Second*30)return nil
}func (this *ServerLogic) Close() {this.cancel()this.waiter.Wait()
}func (this *ServerLogic) PutData(user UserData) error {select {case this.ch<-user:return nildefault:return fmt.Errorf("queue overflow")}
}

如果有人想直接使用的话,只需要把UserData struct换成自己的请求数据,把Dothing里面的代码换成让goroutine多任务执行的代码就可以在自己的项目中使用了。
在这里插入图片描述
PutData有请求数据就放入channel,每个goroutine不停的循环从channel里面取数据,取到数据之后就执行相应的逻辑流程,可以看到整体的调度都是channel来控制的,通过channel的通信来传递数据。

不要通过共享内存来通信,要通过通信来共享内存

看看大概的代码分析

  1. InitWorker的时候会创建queue个channl,再创建workers个goroutine,执行go Proc()
  2. Proc方法,里面有for的无限循环,不停从步骤1里面创建的channl里面获取UserData数据,一旦获取数据成功,就会带着UserData数据去执行Dothing方法。需要注意的是,这是workers个goroutine都在执行Proc
  3. Dothing方法,就是让某一个goroutine拿到UserData数据去处理数据,执行逻辑
  4. Close方法,给所有的goroutine发送关闭的信号,channl里面不在有数据写入,waiter.Wait()等待现有的channel里面数据被消费完,goroutine就执行完毕退出。
  5. PutData方法,就是把请求的数据交给goroutine去执行。具体的做法,是把数据 塞到channl队列里面,如果queue个channl队列已满,就抛出溢出错误。

当然了PutData也可以等待channl队列里面的数据被Proc拿出,然后空出位置再塞数据到channl队列。

func (this *ServerLogic) PutData(user UserData) error {timer := time.NewTimer(3*time.Second)select {case this.ch<-user:return nilcase <-timer.C:return fmt.Errorf("put timeout")}
}

加一个超时器,总不能等到天荒地老把,如果超过三秒,仍然没有空出channl位置,现有的队列还没有消费完,就抛出塞数据超时的错误.

看一下样例的使用的代码

package mainimport (context2 "context""fmt""test/logic"
)func main() {context := context2.Background()server := logic.NewServerLogic(&context, 1, 2)rt1 := server.PutData(logic.UserData{Age:     11,Name:    "test1",Postion: "golang",})fmt.Println(rt1)rt2 := server.PutData(logic.UserData{Age:     12,Name:    "test2",Postion: "golang",})fmt.Println(rt2)rt3 := server.PutData(logic.UserData{Age:     13,Name:    "test3",Postion: "golang",})fmt.Println(rt3)server.Close()fmt.Println("end")
}

等待了大概三十多秒之后的结果,打印结果其实跟预想的是一样的。

<nil>
<nil>
queue overflow
end

NewServerLogic(&context, 1, 2)代码中,我们要求创建了1个goroutine,大小为2的channl队列。
所以第一个PutData和第二个PutData是塞数据成功的。等到第三次PutData的时候,因为我们channl队列的大小是2,已经被占满了,所以第三次就会提示溢出错误。

使用goroutine另一种方法

我看项目中还有一些其他人的使用方法,区别只是退出的时候没有使用context的cancel方法,而是使用了channel去通知退出goroutine,内部的原理其实是一样的。看一下下面的代码。
在这里插入图片描述
只有关闭这里是不一样的,其他的基本一致。执行退出的时候在Close()方法中,close(this.quit)会给quit channel写入数据,Proc()方法会循环从channel和quit里面取数据,一旦从this.quit里面取出了数据,说明系统让关闭goroutine,然后Proc方法就终止。

go func()行不行

有人说,扯这么多,为啥go func()不行,我在项目里面使用go func()运行的好好,而且golang的HTTP库里也是使用的go c.serve(ctx)。
我的理解是主要看使用场景,如果你的服务对结果要求不是100%的成功,对并发的要求很高,那就可以使用go func(),go c.serve(ctx)也是类似,TCP本身就是不可靠的连接,HTTP也允许有极少量的失败状态。

如果你的服务里面只是想让多个goroutine处理你的数据,不希望这个goroutine太多影响你的主干服务,或者你为了提高数据处理效率,想让多个goroutine去请求第三方的服务,这样的话,就应该创建若干个goroutine去并发处理你的任务,也不建议直接go func(),goroutine数量不可控,会影响其他的主干服务或者占用服务器资源,如果请求第三方的服务,可能会因为并发太高被限制,或者把第三方服务打挂。我们就遇到过这种情况。
总之,使用场景很重要,不是一概而论的。

这篇关于golang开发:goroutine在项目中的使用姿势的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/796425

相关文章

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完

使用docker搭建嵌入式Linux开发环境

《使用docker搭建嵌入式Linux开发环境》本文主要介绍了使用docker搭建嵌入式Linux开发环境,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录1、前言2、安装docker3、编写容器管理脚本4、创建容器1、前言在日常开发全志、rk等不同

使用Python实现Word文档的自动化对比方案

《使用Python实现Word文档的自动化对比方案》我们经常需要比较两个Word文档的版本差异,无论是合同修订、论文修改还是代码文档更新,人工比对不仅效率低下,还容易遗漏关键改动,下面通过一个实际案例... 目录引言一、使用python-docx库解析文档结构二、使用difflib进行差异比对三、高级对比方