在我的代码中,安全完成goroutines的正确方法是什么?
我正在编写一个简单的tcp服务器,goroutine模型非常简单: 一个goroutine负责接受新的连接;对于每个新连接,将启动三个goroutine:在我的代码中,安全完成goroutines的正确方法是什么?,go,channel,goroutine,Go,Channel,Goroutine,我正在编写一个简单的tcp服务器,goroutine模型非常简单: 一个goroutine负责接受新的连接;对于每个新连接,将启动三个goroutine: 一个供阅读 一个用于处理和处理应用程序逻辑 一个用来写字的 目前一台服务器最多只能服务1000个用户,所以我不想限制goroutine的数量 for { conn, err := listener.Accept() // .... connHandler := connHandler{ conn:
for {
conn, err := listener.Accept()
// ....
connHandler := connHandler{
conn: conn,
done: make(chan struct{}),
readChan: make(chan string, 100),
writeChan: make(chan string, 100),
}
// ....
go connHandler.readAll()
go connHandler.processAll()
go connHandler.writeAll()
}
我使用done
频道通知所有三个频道完成,当用户注销或发生永久性网络错误时,done
频道将关闭(使用sync.one确保只关闭一次):
下面是writeAll()
方法的代码:
func (connHandler *connHandler) writeAll() {
writer := bufio.NewWriter(connHandler.conn)
for {
select {
case <-connHandler.done:
connHandler.conn.Close()
return
case msg := <-connHandler.writeChan:
connHandler.writeOne(msg, writer)
}
}
}
Send
方法将主要在processAll()
goroutine中调用,但在许多其他goroutine中也会调用,因为不同的用户需要相互通信
现在的问题是:如果userA注销或网络失败,userB向userA发送消息,userB的goroutine可能会被永久阻止,因为没有人会从通道接收消息
func (connHandler *connHandler) Send(msg string) {
select {
case connHandler.writeChan <- msg:
case <- connHandler.done:
log.Debug("connHandler is done, exiting Send without sending.")
case <-time.After(10 * time.Second):
log.Warning(connHandler.Addr() + " send msg timeout:" + msg)
}
}
我的解决方案:
我的第一个想法是使用布尔值来确保connHanler在发送到它时没有关闭:
func (connHandler *connHandler) Send(msg string) {
if !connHandler.isClosed {
connHandler.writeChan <- msg
}
}
现在我觉得代码是安全的,但也很难看,每次发送消息时都会启动计时器,这感觉像是不必要的开销
然后我读了这篇文章:,我的问题看起来像文章中的第二个例子:
一个接收者,N个发送者,接收者说“请停止发送更多”
通过关闭额外的信号通道
但我认为,在我的情况下,这种解决方案无法消除阻塞的可能性
也许最简单的解决方案是关闭写入通道,让发送
方法死机,然后使用恢复
来处理死机?但这看起来也是一种丑陋的方式
那么,有没有一种简单直接的方法来完成我想做的事情呢
(我的英语不好,所以如果有任何歧义,请指出,谢谢。)你的例子看起来很好,我认为你已经得到了你需要的90% 我认为你看到的问题是发送,而实际上你可能已经“完成” 您可以使用“完成”频道通知所有已完成的围棋程序。您将始终能够从闭合通道读取值(它将是零值)。这意味着您可以更新<代码>发送(MSG)< /Cord>方法,以考虑已完成的通道。< /P>
func (connHandler *connHandler) Send(msg string) {
select {
case connHandler.writeChan <- msg:
case <- connHandler.done:
log.Debug("connHandler is done, exiting Send without sending.")
case <-time.After(10 * time.Second):
log.Warning(connHandler.Addr() + " send msg timeout:" + msg)
}
}
func(connHandler*connHandler)发送(消息字符串){
挑选{
您的Send()
中的case connHandler.writeChan可能您可以在接收用户的connHandler.done
上选择。我认为使用这里的选择确实解决了我的问题,很简单,但我没有看到它!非常感谢。
func (connHandler *connHandler) Send(msg string) {
if !connHandler.isClosed {
timer := time.NewTimer(10 * time.Second)
defer timer.Stop()
select {
case connHandler.writeChan <- msg:
case <-timer.C:
log.Warning(connHandler.Addr() + " send msg timeout:" + msg)
}
}
}
func (connHandler *connHandler) Send(msg string) {
select {
case connHandler.writeChan <- msg:
case <- connHandler.done:
log.Debug("connHandler is done, exiting Send without sending.")
case <-time.After(10 * time.Second):
log.Warning(connHandler.Addr() + " send msg timeout:" + msg)
}
}