英文:
golang 2 go routines if one terminates terminate the other one
问题
所以我正在使用gorilla的websocket库,我正在构建一个websocket服务器。当我接收到一个连接时,我创建两个goroutine,一个用于从客户端读取传入的消息,另一个用于监听传入的消息并将其发送到客户端。
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
go p.readFromSocket()
go p.writeToSocket()
//blocks until we receive an interrupt from the read channel
<-p.closeEventChan
p.cleanup() //nothing else to do so lets cleanup
}
func (p *Player) writeToSocket() {
for m := range p.writeChan {
if p.conn == nil {
break
}
if reflect.DeepEqual(network.Packet{}, m) == true {
break
}
if err := p.conn.WriteJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) readFromSocket() {
for {
if p.conn == nil {
break
}
m := network.Packet{}
if err := p.conn.ReadJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) Cleanup() {
//make sure the channels get a close message which will break us out of the go routines
close(p.writeChan)
close(p.closeEventChan)
//only close the connection if we have a connection
if p.conn != nil {
p.conn.Close()
p.conn = nil
}
}
我的问题是,如果我们离开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.
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
go p.readFromSocket()
go p.writeToSocket()
//blocks until we receive an interrupt from the read channel
<-p.closeEventChan
p.cleanup() //nothing else to do so lets cleanup
}
func (p *Player) writeToSocket() {
for m := range p.writeChan {
if p.conn == nil {
break
}
if reflect.DeepEqual(network.Packet{}, m) == true {
break
}
if err := p.conn.WriteJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) readFromSocket() {
for {
if p.conn == nil {
break
}
m := network.Packet{}
if err := p.conn.ReadJSON(m); err != nil {
break
}
}
p.closeEventChan <- true
}
func (p *Player) Cleanup() {
//make sure the channels get a close message which will break us out of the go routines
close(p.writeChan)
close(p.closeEventChan)
//only close the connection if we have a connection
if p.conn != nil {
p.conn.Close()
p.conn = nil
}
}
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
你通常可以使用共享的退出通道和一些计数来实现这一点。
func (p *Player) writeToSocket(quit <-chan struct{}) {
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
case m := <-p.writeChan:
// 做一些操作
// writeChan关闭但quit没有关闭的情况
default:
return
}
}
}
func (p *Player) readFromSocket(quit <-chan struct{}) {
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
default:
// 做一些操作
}
}
}
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
quit := make(chan struct{})
go p.readFromSocket(quit)
go p.writeToSocket(quit)
// 阻塞直到从读通道接收到中断信号
<-p.closeEventChan
close(quit)
// 这里是多余的,但在不同的情况下,你可以使用NumWorkers变量循环遍历剩余的工作线程数
for i := 0; i < 1; i++ {
<-p.closeEventChan
}
p.cleanup() // 没有其他事情要做,所以进行清理
}
这里的思路是:
- 所有工作线程在返回之前通过一个通道向中央循环发送关闭信号。
- 所有工作线程在收到退出消息(通过关闭通道)后返回。
- 中央循环在第一个工作线程退出后关闭退出通道,然后等待其他工作线程退出。
英文:
You can usually do this with a shared quit channel, and some counting.
func (p *Player) writeToSocket(quit <-chan struct{})
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
case m := <-p.writeChan:
// Do stuff
// Case where writeChan is closed, but quit isn't
default:
return
}
}
}
func (p *Player) readFromSocket(quit <-chan struct{})
defer func() {
p.closeEventChan <- true
}()
for {
select {
case <-quit:
return
default:
// Do stuff
}
}
}
func (p *Player) EventLoop() {
l4g.Info("Starting player %s event loop", p)
quit := make(chan struct{})
go p.readFromSocket(quit)
go p.writeToSocket(quit)
//blocks until we receive an interrupt from the read channel
<-p.closeEventChan
close(quit)
// This is superfluous here, but in a different case
// you loop over the remaining number of workers using a NumWorkers variable
for i := 0; i < 1; i++ {
<-p.closeEventChan
}
p.cleanup() //nothing else to do so lets cleanup
}
The idea here is that:
- All workers notify the central routine upon close via a channel before returning.
- All workers return upon a quit message being broadcast (via close).
- The central loop closes the quit channel after the first worker exits, then waits for the rest to exit.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论