经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » Go语言 » 查看文章
GOLANG使用Context管理关联goroutine的方法
来源:jb51  时间:2019/1/15 9:19:19  对本文有异议

一般一个业务很少不用到goroutine的,因为很多方法是需要等待的,例如http.Server.ListenAndServe这个就是等待的,除非关闭了Server或Listener,否则是不会返回的。除非是一个API服务器,否则肯定需要另外起goroutine发起其他的服务,而且对于API服务器来说,在http.Handler的处理函数中一般也需要起goroutine,如何管理这些goroutine,在GOLANG1.7提供context.Context

先看一个简单的,如果启动两个goroutine,一个是HTTP,还有个信号处理的收到退出信号做清理:

  1. wg := sync.WaitGroup{}
  2. defer wg.Wait()
  3.  
  4. wg.Add(1)
  5. go func() {
  6. defer wg.Done()
  7.  
  8. ss := make(os.Signal, 0)
  9. signal.Notify(ss, syscall.SIGINT, syscall.SIGTERM)
  10. for s := ss {
  11. fmt.Println("Got signal", s)
  12. break
  13. }
  14. }()
  15.  
  16. wg.Add(1)
  17. go func() {
  18. defer wg.Done()
  19.  
  20. svr := &http.Server{ Addr:":8080", Handler:nil, }
  21. fmt.Println(svr.ListenAndServe())
  22. }
  23.  

很清楚,起了两个goroutine,然后用WaitGroup等待它们退出。如果它们之间没有交互,不互相影响,那真的是蛮简单的,可惜这样是不行的,因为信号的goroutine收到退出信号后,应该通知server退出。暴力一点的是直接调用svr.Close(),但是如果有些请求还需要取消怎么办呢?最好用Context了:

  1. wg := sync.WaitGroup{}
  2. defer wg.Wait()
  3.  
  4. ctx,cancel := context.WithCancel(context.Background())
  5.  
  6. wg.Add(1)
  7. go func() {
  8. defer wg.Done()
  9.  
  10. ss := make(chan os.Signal, 0)
  11. signal.Notify(ss, syscall.SIGINT, syscall.SIGTERM)
  12. select {
  13. case <- ctx.Done():
  14. return
  15. case s := <- ss:
  16. fmt.Println("Got signal", s)
  17. cancel() // 取消请求,通知用到ctx的所有goroutine
  18. return
  19. }
  20. }()
  21.  
  22. wg.Add(1)
  23. go func() {
  24. defer wg.Done()
  25. defer cancel()
  26.  
  27. svr := &http.Server{ Addr:":8080", Handler:nil, }
  28.  
  29. go func(){
  30. select {
  31. case <- ctx.Done():
  32. svr.Close()
  33. }
  34. }
  35.  
  36. fmt.Println(svr.ListenAndServe())
  37. }

这个方式可以在新开goroutine时继续使用,譬如新加一个goroutine,里面读写了UDPConn:

  1. wg.Add(1)
  2. go func() {
  3. defer wg.Done()
  4. defer cancel()
  5.  
  6. var conn *net.UDPConn
  7. if conn,err = net.Dial("udp", "127.0.0.1:1935"); err != nil {
  8. fmt.Println("Dial UDP server failed, err is", err)
  9. return
  10. }
  11.  
  12. fmt.Println(UDPRead(ctx, conn))
  13. }()
  14.  
  15. UDPRead = func(ctx context.Context, conn *net.UDPConn) (err error) {
  16. wg := sync.WaitGroup{}
  17. defer wg.Wait()
  18.  
  19. ctx, cancel := context.WithCancel(ctx)
  20.  
  21. wg.Add(1)
  22. go func() {
  23. defer wg.Done()
  24. defer cancel()
  25.  
  26. for {
  27. b := make([]byte, core.MTUSize)
  28. size, _, err := conn.ReadFromUDP(b)
  29. // 处理UDP包 b[:size]
  30. }
  31. }()
  32.  
  33. select {
  34. case <-ctx.Done():
  35. conn.Close()
  36. }
  37. return
  38. }
  39.  

如果只是用到HTTP Server,可以这么写:

  1. func run(ctx contex.Context) {
  2. server := &http.Server{Addr: addr, Handler: nil}
  3. go func() {
  4. select {
  5. case <-ctx.Done():
  6. server.Close()
  7. }
  8. }()
  9.  
  10. http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
  11. })
  12.  
  13. fmt.Println(server.ListenAndServe())
  14. }
  15.  

如果需要提供一个API来让服务器退出,可以这么写:

  1. func run(ctx contex.Context) {
  2. server := &http.Server{Addr: addr, Handler: nil}
  3.  
  4. ctx, cancel := context.WithCancel(ctx)
  5. http.HandleFunc("/quit", func(w http.ResponseWriter, r *http.Request) {
  6. cancel() // 使用局部的ctx和cancel
  7. })
  8.  
  9. go func() {
  10. select {
  11. case <-ctx.Done():
  12. server.Close()
  13. }
  14. }()
  15.  
  16. fmt.Println(server.ListenAndServe())
  17. }
  18.  

使用局部的ctx和cancel,可以避免cancel传入的ctx,只是影响当前的ctx。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持w3xue。

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

本站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号