经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
golang开发_goroutine在项目中的使用姿势
来源:cnblogs  作者:飞翔码农  时间:2024/3/11 8:50:52  对本文有异议

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

goroutine在项目中的使用方法

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

  1. package logic
  2. import (
  3. "context"
  4. "fmt"
  5. "sync"
  6. "time"
  7. )
  8. type UserData struct {
  9. Age int
  10. Name string
  11. Postion string
  12. }
  13. type ServerLogic struct {
  14. ctx context.Context
  15. cancel func()
  16. waiter sync.WaitGroup
  17. ch chan UserData
  18. }
  19. func NewServerLogic(logCtx *context.Context, worker int, queue int) *ServerLogic {
  20. logic := &ServerLogic{}
  21. logic.InitWorker(worker, queue)
  22. return logic
  23. }
  24. func (this *ServerLogic) InitWorker(workers int, queue int) {
  25. this.ch = make(chan UserData, queue)
  26. this.ctx, this.cancel = context.WithCancel(context.Background())
  27. this.waiter.Add(workers)
  28. for i := 0; i < workers; i++ {
  29. go this.Proc()
  30. }
  31. }
  32. func (this *ServerLogic) Proc() {
  33. defer this.waiter.Done()
  34. for {
  35. select {
  36. case t := <-this.ch:
  37. this.Dothing(t)
  38. case <-this.ctx.Done():
  39. return
  40. }
  41. }
  42. }
  43. func (this *ServerLogic) Dothing(data UserData) error {
  44. //do code
  45. time.Sleep(time.Second*30)
  46. return nil
  47. }
  48. func (this *ServerLogic) Close() {
  49. this.cancel()
  50. this.waiter.Wait()
  51. }
  52. func (this *ServerLogic) PutData(user UserData) error {
  53. select {
  54. case this.ch<-user:
  55. return nil
  56. default:
  57. return fmt.Errorf("queue overflow")
  58. }
  59. }

如果有人想直接使用的话,只需要把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队列。

  1. func (this *ServerLogic) PutData(user UserData) error {
  2. timer := time.NewTimer(3*time.Second)
  3. select {
  4. case this.ch<-user:
  5. return nil
  6. case <-timer.C:
  7. return fmt.Errorf("put timeout")
  8. }
  9. }

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

看一下样例的使用的代码

  1. package main
  2. import (
  3. context2 "context"
  4. "fmt"
  5. "test/logic"
  6. )
  7. func main() {
  8. context := context2.Background()
  9. server := logic.NewServerLogic(&context, 1, 2)
  10. rt1 := server.PutData(logic.UserData{
  11. Age: 11,
  12. Name: "test1",
  13. Postion: "golang",
  14. })
  15. fmt.Println(rt1)
  16. rt2 := server.PutData(logic.UserData{
  17. Age: 12,
  18. Name: "test2",
  19. Postion: "golang",
  20. })
  21. fmt.Println(rt2)
  22. rt3 := server.PutData(logic.UserData{
  23. Age: 13,
  24. Name: "test3",
  25. Postion: "golang",
  26. })
  27. fmt.Println(rt3)
  28. server.Close()
  29. fmt.Println("end")
  30. }

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

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

原文链接:https://www.cnblogs.com/feixiangmanon/p/18064193

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号