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

goroutine在项目中的使用方法

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

package logic

import (
"context"
"fmt"
"sync"
"time"
) type UserData struct {
Age int
Name string
Postion string
} type ServerLogic struct {
ctx context.Context
cancel func()
waiter sync.WaitGroup
ch 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 code
time.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 nil
default:
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 nil
case <-timer.C:
return fmt.Errorf("put timeout")
}
}

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

看一下样例的使用的代码

package main

import (
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在项目中的使用姿势的更多相关文章

  1. 带你了解关于FastAPI快速开发Web API项目中的模板和Jinja

    摘要:FastAPI 实际上是为构建 API 和微服务而设计的.它可用于构建使用 Jinja 提供 HTML 服务的 Web 应用程序. 本文分享自华为云社区<FastAPI 快速开发 Web ...

  2. 企业应用开发模式 ERP项目中应用到的技术和工具

    一.基础技术选型 C# .NET 3.5/4.0  这两个版本的.NET已经相当方便(Linq, Lambda,Parallel),语法简洁,配合WCF和WF两项技术,可以满足快速开发,维护方便的目标 ...

  3. cocos2d-x开发: 如何从项目中分离出接口范例

    cocos2d-x开发,包括核心模块接口开发和脚本部分的业务逻辑实现.从上层应用需求开始说,脚本在做业务逻辑实现的时候, 很多时候都需要依赖底层的接口功能,但是不是所有的人都可以游刃有余的去明白该怎么 ...

  4. C#.NET常见问题(FAQ)-使用SharpDevelop开发 如何在项目中添加类文件

    点击文件-新建-文件,然后再工程内创建文件   或者工程-添加-新建项     更多教学视频和资料下载,欢迎关注以下信息: 我的优酷空间: http://i.youku.com/acetaohai12 ...

  5. 【Vue】vue项目目录介绍 es6的导入导出语法 vue项目开发规范 Vue项目编写步骤

    目录 昨日回顾 今日内容 0 vue-cli创建项目 node.js环境 创建vue-cli项目 1 vue项目目录介绍 node_modules index.html app.vue package ...

  6. 【菜鸟玩Linux开发】在Linux中使用VS Code编译调试C++项目

    最近项目需求,需要在Linux下开发C++相关项目,经过一番摸索,简单总结了一下如何通过VS Code进行编译调试的一些注意事项. 关于VS Code在Linux下的安装这里就不提了,不管是CentO ...

  7. Android 百度地图开发(一)--- 申请API Key和在项目中显示百度地图

      标签: Android百度地图API Key  分类: Android 百度地图开发(2)    最近自己想研究下地图,本来想研究google Map,但是申请API key比较坑爹,于是从百度地 ...

  8. 在web项目中使用cxf开发webservice,包含spring支持

    本文主要介绍了,如何使用cxf内置的例子,学会开发webserivce,在web项目中使用,且包含spring支持. webserivce的开发可以使用cxf或者axis,好像还有httpclient ...

  9. VUE依赖webpack分别给开发环境和生产环境配置不同的常量值并在项目中动态引用

    当在开发和产品上线的时候,我们经常会遇到在同一个地方由于环境的不同而地址也不同的情况,这时候如果在代码中将该地址写死,那势必会造成上线时手动改动,多人开发及多处使用该地址难以维护等一系列问题,为避免这 ...

  10. angular+ionic前后端分离开发项目中的使用

    Ionic基于AngularJS构建而成,所以学习一些AngularJS的知识很有必要.Ionic并没有独立开发一套完整的Web应用框架,而是对AngularJS进行了扩展,给它添加了大量界面组件和其 ...

随机推荐

  1. 用Unity3D做游戏开发在Android上的常用调试方法

    Hdg Remote Debug 远程调试 游戏运行在手机上,可以通过pc端的unity来随时修改当前场景中GameObject的变量,从而改变手机上运行时的表现.比如,我可以勾掉下图中的" ...

  2. 报错ValueError: Can't find 'adapter_config.json'

    前言 在做组内2030项目时,我具体做的一个工作是对大模型进行LoRA微调,在整个过程中有许多坑,其中有些值得记录的问题,于是便产生了这篇博客. 问题 我在得到微调好的模型后,需要对模型进行性能测评. ...

  3. 【8】python_matplotlib改变横坐标和纵坐标上的刻度(ticks)、sagemath-list_plot()调整图例(legend)中点的数量、Matplotlib画各种论文图

    1.python_matplotlib改变横坐标和纵坐标上的刻度(ticks) 用matplotlib画二维图像时,默认情况下的横坐标和纵坐标显示的值有时达不到自己的需求,需要借助xticks()和y ...

  4. 8.4 Windows驱动开发:文件微过滤驱动入门

    MiniFilter 微过滤驱动是相对于SFilter传统过滤驱动而言的,传统文件过滤驱动相对来说较为复杂,且接口不清晰并不符合快速开发的需求,为了解决复杂的开发问题,微过滤驱动就此诞生,微过滤驱动在 ...

  5. Rsync+Inotify 实现数据同步

    Rsync 是UNIX及类UNIX-Like平台下一款强大的数据镜像备份软件,它不像FTP或其他文件传输服务那样需要进行全备份,Rsync 可以根据数据的变化进行差异备份,从而减少数据流量,提高工作效 ...

  6. LyScriptTools 模块类API接口手册

    LyScriptTools工具包是在LyScript模块基础上封装的工具包,其主要是二次封装LyScript插件实现的一些新功能,或者将特定功能组件拆分开形成的独立模块,此类模块可实现更加精细化的功能 ...

  7. Azure Data Factory(十二)传参调用 Azure Function

    一,引言 在实际的项目中,Azure Data Factroy 中的 Data Flow 并不能彻底帮我们完成一系列复制逻辑计算, 比如我们需要针对数据集的每一行数据进行判断计算,Data Flow ...

  8. MySQL-生成随机数字、字符串、日期、验证码及 UUID的方法

    一.生成随机数字 1. 生成 0 到 1 之间的随机数 MySQL 中的 RAND 函数可以用于生成一个大于等于 0 小于 1 的随机数字.例如: SELECT rand(); 该函数返回的数据类型为 ...

  9. 扩展说明: 指令微调 Llama 2

    这篇博客是一篇来自 Meta AI,关于指令微调 Llama 2 的扩展说明.旨在聚焦构建指令数据集,有了它,我们则可以使用自己的指令来微调 Llama 2 基础模型. 目标是构建一个能够基于输入内容 ...

  10. 云计算 - 负载均衡SLB方案全解与实战

    云计算 - 负载均衡SLB方案全解与实战,介绍SLB的核心技术.用户最佳实践.阿里云 SLB产品举例.应用场景. 关注[TechLeadCloud],分享互联网架构.云服务技术的全维度知识.作者拥有1 ...