Bad*_*Zen 8 concurrency tcp go
看着net.TCPListener。考虑到 Go 并发范例,人们会期望将此系统功能实现为通道,以便您chan *net.Conn从Listen()函数或类似的东西中获得 a 。
但看起来 Accept() 就是这样,而且只是阻塞,就像系统接受一样。除了它是残废的,因为:
所以我正在做类似的事情:
acceptChannel = make(chan *Connection)
go func() {
for {
rw, err := listener.Accept()
if err != nil { ... handle error ... close(acceptChannel) ... return }
s.acceptChannel <-&Connection{tcpConn: rw, .... }
}
}()
Run Code Online (Sandbox Code Playgroud)
这样我就可以在选择中使用多个服务器套接字,或者将 Accept() 上的等待与其他通道复用。我错过了什么吗?我是 Go 的新手,所以我可能会忽略一些事情 - 但 Go 真的没有用自己的并发范例实现自己的阻塞系统功能吗?我真的需要为每个我想监听的套接字(可能是数百个或数千个)使用一个单独的 goroutine 吗?这是使用正确的习惯用法,还是有更好的方法?
你的代码很好。您甚至可以更进一步并替换:
s.acceptChannel <-&Connection{tcpConn: rw, .... }
Run Code Online (Sandbox Code Playgroud)
和:
go handleConnection(&Connection{tcpConn: rw, .... })
Run Code Online (Sandbox Code Playgroud)
正如评论中提到的,例程不是系统线程,它们是 Go 运行时管理的轻量级线程。当您为每个连接创建例程时,您可以轻松使用阻塞操作,这更容易实现。然后,Go 运行时会为您选择例程,因此您正在寻找的行为只是隐藏在语言中的其他地方。你看不到它,但它无处不在。
现在,如果您需要更复杂的东西,并且根据我们的对话,实现类似于选择超时的东西,您将完全按照您的建议进行:将所有新连接推送到通道并使用计时器对其进行复用。这似乎是 Go 的发展方向。
请注意,如果其中一个接受器失败,则无法关闭接受通道,因为另一个接受器在写入时会出现恐慌。
我的(更完整的)例子:
newConns := make(chan net.Conn)
// For every listener spawn the following routine
go func(l net.Listener) {
for {
c, err := l.Accept()
if err != nil {
// handle error (and then for example indicate acceptor is down)
newConns <- nil
return
}
newConns <- c
}
}(listener)
for {
select {
case c := <-newConns:
// new connection or nil if acceptor is down, in which case we should
// do something (respawn, stop when everyone is down or just explode)
case <-time.After(time.Minute):
// timeout branch, no connection for a minute
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7970 次 |
| 最近记录: |