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数量不可控,会影响其他的主干服务或者占用服务器资源,如果请求第三方的服务,可能会因为并发太高被限制,或者把第三方服务打挂。我们就遇到过这种情况。
总之,使用场景很重要,不是一概而论的。

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

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

相关文章

博士推荐 | 美国知名化工企业研发主管,高分子科学与工程博士

编辑 / 木子 审核 / 朝阳 伟骅英才 伟骅英才致力于以大数据、区块链、AI人工智能等前沿技术打造开放的人力资本生态&#xff0c;用科技解决职业领域问题&#xff0c;提升行业数字化服务水平&#xff0c;提供创新型的产业与人才一体化服务的人力资源解决方案和示范平台&#x…

未来城市:探索数字孪生在智慧城市中的实际应用与价值

目录 一、引言 二、数字孪生与智慧城市的融合 三、数字孪生在智慧城市中的实际应用 1、智慧交通管理 2、智慧能源管理 3、智慧建筑管理 4、智慧城市管理 四、数字孪生在智慧城市中的价值 五、挑战与展望 六、结论 一、引言 随着科技的飞速发展&#xff0c;智慧城市已…

Windows系统安装Tomcat并结合内网穿透实现公网访问本地网页

文章目录 前言1.本地Tomcat网页搭建1.1 Tomcat安装1.2 配置环境变量1.3 环境配置1.4 Tomcat运行测试1.5 Cpolar安装和注册 2.本地网页发布2.1.Cpolar云端设置2.2 Cpolar本地设置 3.公网访问测试4.结语 前言 Tomcat作为一个拥有强大功能的轻量级服务器&#xff0c;由于其可以实…

代码讲解:如何把3D数据转换成旋转的视频?

目录 3D数据集下载 读取binvox文件 使用matplotlib创建图 动画效果 完整代码 3D数据集下载 这里以shapenet数据集为例&#xff0c;可以访问外网的可以去直接申请下载&#xff1b;我也准备了一个备份在百度网盘的数据集&#xff0c;可以参考&#xff1a; ShapeNet简介和下…

【C++庖丁解牛】实现string容器的增删查改 | string容器的基本接口使用

&#x1f4d9; 作者简介 &#xff1a;RO-BERRY &#x1f4d7; 学习方向&#xff1a;致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f4d2; 日后方向 : 偏向于CPP开发以及大数据方向&#xff0c;欢迎各位关注&#xff0c;谢谢各位的支持 目录 前言&#x1f4d6;pu…

数据结构部分

来源地址 一 数据结构 1 堆和树之间的区别 区别就在于树是没有特定顺序的&#xff0c;你需要遍历整个树才能找到特定元素&#xff1b;而堆是有序的&#xff0c;你可以直接找到最大&#xff08;或最小&#xff09;的元素。 堆&#xff1a;假设你正在开发一个任务调度系统&…

2024蓝桥杯每日一题(多路归并)

一、第一题&#xff1a;鱼塘钓鱼 解题思路&#xff1a;多路归并优先队列 首先枚举能走到的距离然后再用优先队列将最大的值累加 【Python程序代码】 from heapq import * n int(input()) a [0] list(map(int,input().split())) b [0] list(map(int,input().spli…

数据结构(九)——单链表的基本操作

&#x1f600;前言 单链表是一种常见的数据结构&#xff0c;它由一系列结点组成&#xff0c;每个结点包含数据元素和指向下一个结点的指针。在本篇文章中&#xff0c;我们将讨论单链表的基本操作&#xff0c;包括初始化、销毁、清空、求表长、按值查找、插入和删除等操作。这些…

NGINX源码安装详细配置文档

NGINX源码安装详细配置文档 一、基础Linux指令 查看nginx进程是否启动&#xff1a;ps -ef | grep nginx 关闭防火墙&#xff1a;systemctl stop firewalld 开放80端口&#xff1a;firewall-cmd --zonepublic --add-port80/tcp --permanent 关闭80端口&#xff1a;firewall-cmd …

AJAX-HTTP协议

文章目录 HTTP协议请求报文响应报文接口文档 HTTP协议 规定了浏览器发送及服务器返回内容的格式 请求报文 浏览器按照HTTP协议要求的格式&#xff0c;发送给服务器的内容 组成部分&#xff1a; 1.请求行&#xff1a;请求方法&#xff0c;URL&#xff0c;协议 2.请求头&#…

数据结构(二)——线性表(顺序表)

二、线性表 2.1线性表的定义和基本操作 2.1.1 线性表的基本概念 线性表&#xff1a;是具有相同数据类型的 n 个数据元素的有限序列。(Eg:所有的整数按递增次序排列&#xff0c;不是顺序表&#xff0c;因为所有的整数是无限的)其中n为表长&#xff0c;当n0时线性表是一个空表…

【NR技术】 3GPP支持无人机服务的关键性能指标

1 性能指标概述 5G系统传输的数据包括安装在无人机上的硬件设备(如摄像头)收集的数据&#xff0c;例如图片、视频和文件。也可以传输一些软件计算或统计数据&#xff0c;例如无人机管理数据。5G系统传输的业务控制数据可基于应用触发&#xff0c;如无人机上设备的开关、旋转、升…