golang 2 go routines if one terminates terminate the other one

huangapple go评论135阅读模式
英文:

golang 2 go routines if one terminates terminate the other one

问题

所以我正在使用gorilla的websocket库,我正在构建一个websocket服务器。当我接收到一个连接时,我创建两个goroutine,一个用于从客户端读取传入的消息,另一个用于监听传入的消息并将其发送到客户端。

  1. func (p *Player) EventLoop() {
  2. l4g.Info("Starting player %s event loop", p)
  3. go p.readFromSocket()
  4. go p.writeToSocket()
  5. //blocks until we receive an interrupt from the read channel
  6. <-p.closeEventChan
  7. p.cleanup() //nothing else to do so lets cleanup
  8. }
  9. func (p *Player) writeToSocket() {
  10. for m := range p.writeChan {
  11. if p.conn == nil {
  12. break
  13. }
  14. if reflect.DeepEqual(network.Packet{}, m) == true {
  15. break
  16. }
  17. if err := p.conn.WriteJSON(m); err != nil {
  18. break
  19. }
  20. }
  21. p.closeEventChan <- true
  22. }
  23. func (p *Player) readFromSocket() {
  24. for {
  25. if p.conn == nil {
  26. break
  27. }
  28. m := network.Packet{}
  29. if err := p.conn.ReadJSON(m); err != nil {
  30. break
  31. }
  32. }
  33. p.closeEventChan <- true
  34. }
  35. func (p *Player) Cleanup() {
  36. //make sure the channels get a close message which will break us out of the go routines
  37. close(p.writeChan)
  38. close(p.closeEventChan)
  39. //only close the connection if we have a connection
  40. if p.conn != nil {
  41. p.conn.Close()
  42. p.conn = nil
  43. }
  44. }

我的问题是,如果我们离开readFromSocket()循环,Cleanup()会被调用,但我们永远不会离开writeToSocket()循环!这个问题可以在这个Go Playground中更简单地演示:https://play.golang.org/p/49bh7bbbG-

我们如何修复这个问题,以便如果我们离开writeToSocket()循环,我们也离开readFromSocket()循环,反之亦然?

我曾经以为这样做会起作用,因为如果你在一个通道上调用close(p.writeChan),通道接受的默认值将被发送。

英文:

so I am using gorillas websocket library and I'm building a websocket server when I receive a connection I create 2 go routines one to read incoming messages from the client and another 1 to listen for in coming messages sent to the channel and then send them to the client.

  1. func (p *Player) EventLoop() {
  2. l4g.Info(&quot;Starting player %s event loop&quot;, p)
  3. go p.readFromSocket()
  4. go p.writeToSocket()
  5. //blocks until we receive an interrupt from the read channel
  6. &lt;-p.closeEventChan
  7. p.cleanup() //nothing else to do so lets cleanup
  8. }
  9. func (p *Player) writeToSocket() {
  10. for m := range p.writeChan {
  11. if p.conn == nil {
  12. break
  13. }
  14. if reflect.DeepEqual(network.Packet{}, m) == true {
  15. break
  16. }
  17. if err := p.conn.WriteJSON(m); err != nil {
  18. break
  19. }
  20. }
  21. p.closeEventChan &lt;- true
  22. }
  23. func (p *Player) readFromSocket() {
  24. for {
  25. if p.conn == nil {
  26. break
  27. }
  28. m := network.Packet{}
  29. if err := p.conn.ReadJSON(m); err != nil {
  30. break
  31. }
  32. }
  33. p.closeEventChan &lt;- true
  34. }
  35. func (p *Player) Cleanup() {
  36. //make sure the channels get a close message which will break us out of the go routines
  37. close(p.writeChan)
  38. close(p.closeEventChan)
  39. //only close the connection if we have a connection
  40. if p.conn != nil {
  41. p.conn.Close()
  42. p.conn = nil
  43. }
  44. }

my problem is if we leave the readFromSocket() loop Cleanup() is called however we never leave the writeToSocket() loop ! this problem can be simpler demonstrated in this go playground https://play.golang.org/p/49bh7bbbG-

how can we fix this so if we leave the writeToSocket() loop we also leave the readFromSocket() loop and vice vesa?

I was under the impression that this would work as if you call close on a channel (close(p.writeChan)) the default value that the channel accepts will be sent

答案1

得分: 3

你通常可以使用共享的退出通道和一些计数来实现这一点。

  1. func (p *Player) writeToSocket(quit <-chan struct{}) {
  2. defer func() {
  3. p.closeEventChan <- true
  4. }()
  5. for {
  6. select {
  7. case <-quit:
  8. return
  9. case m := <-p.writeChan:
  10. // 做一些操作
  11. // writeChan关闭但quit没有关闭的情况
  12. default:
  13. return
  14. }
  15. }
  16. }
  17. func (p *Player) readFromSocket(quit <-chan struct{}) {
  18. defer func() {
  19. p.closeEventChan <- true
  20. }()
  21. for {
  22. select {
  23. case <-quit:
  24. return
  25. default:
  26. // 做一些操作
  27. }
  28. }
  29. }
  30. func (p *Player) EventLoop() {
  31. l4g.Info("Starting player %s event loop", p)
  32. quit := make(chan struct{})
  33. go p.readFromSocket(quit)
  34. go p.writeToSocket(quit)
  35. // 阻塞直到从读通道接收到中断信号
  36. <-p.closeEventChan
  37. close(quit)
  38. // 这里是多余的,但在不同的情况下,你可以使用NumWorkers变量循环遍历剩余的工作线程数
  39. for i := 0; i < 1; i++ {
  40. <-p.closeEventChan
  41. }
  42. p.cleanup() // 没有其他事情要做,所以进行清理
  43. }

这里的思路是:

  1. 所有工作线程在返回之前通过一个通道向中央循环发送关闭信号。
  2. 所有工作线程在收到退出消息(通过关闭通道)后返回。
  3. 中央循环在第一个工作线程退出后关闭退出通道,然后等待其他工作线程退出。
英文:

You can usually do this with a shared quit channel, and some counting.

  1. func (p *Player) writeToSocket(quit &lt;-chan struct{})
  2. defer func() {
  3. p.closeEventChan &lt;- true
  4. }()
  5. for {
  6. select {
  7. case &lt;-quit:
  8. return
  9. case m := &lt;-p.writeChan:
  10. // Do stuff
  11. // Case where writeChan is closed, but quit isn&#39;t
  12. default:
  13. return
  14. }
  15. }
  16. }
  17. func (p *Player) readFromSocket(quit &lt;-chan struct{})
  18. defer func() {
  19. p.closeEventChan &lt;- true
  20. }()
  21. for {
  22. select {
  23. case &lt;-quit:
  24. return
  25. default:
  26. // Do stuff
  27. }
  28. }
  29. }
  30. func (p *Player) EventLoop() {
  31. l4g.Info(&quot;Starting player %s event loop&quot;, p)
  32. quit := make(chan struct{})
  33. go p.readFromSocket(quit)
  34. go p.writeToSocket(quit)
  35. //blocks until we receive an interrupt from the read channel
  36. &lt;-p.closeEventChan
  37. close(quit)
  38. // This is superfluous here, but in a different case
  39. // you loop over the remaining number of workers using a NumWorkers variable
  40. for i := 0; i &lt; 1; i++ {
  41. &lt;-p.closeEventChan
  42. }
  43. p.cleanup() //nothing else to do so lets cleanup
  44. }

The idea here is that:

  1. All workers notify the central routine upon close via a channel before returning.
  2. All workers return upon a quit message being broadcast (via close).
  3. The central loop closes the quit channel after the first worker exits, then waits for the rest to exit.

huangapple
  • 本文由 发表于 2014年12月27日 04:19:52
  • 转载请务必保留本文链接:https://go.coder-hub.com/27661569.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定