我知道我可以使用 Go 中的语法等待多个通道,并使用或类似函数select {}
等待多个文件描述符。syscall.Select()
但是有可能同时在两个频道上等待吗?
作为背景,我希望有一个 goroutine 通过通道接受消息并通过套接字连接(由gozmq提供)转发它们,同时等待套接字连接的回复。
由于底层库的线程安全要求,一次只能在一个线程中访问套接字,这就是为什么我想知道是否有办法从单个 goroutine 处理这个问题。
不可能同时选择通道和文件描述符,因为抽象处于不同的级别。通道由 go 运行时处理,文件描述符由操作系统处理。您需要在它们之间架起一座桥梁,这可以通过net.Pipe()
.
您需要做的几乎就是将一个 goroutine 专用于epoll()
/select()
来监视您的 zmq-sockets 和一个单一的 "wake up" net.Pipe()
。这是您的投票服务器。另一个 goroutine 监听你的读写通道。当有人在读或写通道上发送时,第二个 goroutine 将在管道上发送以唤醒轮询服务器。
这就是标准库中的 net 包的工作方式。我强烈建议阅读它以获得灵感(或窃取...... BSD 许可证非常自由)。
这是网络本身对 pollServer 的描述。您可能需要阅读代码才能理解它在说什么,但本节fd.go
应该是开始查找的好地方。
// A pollServer helps FDs determine when to retry a non-blocking
// read or write after they get EAGAIN. When an FD needs to wait,
// send the fd on s.cr (for a read) or s.cw (for a write) to pass the
// request to the poll server. Then receive on fd.cr/fd.cw.
// When the pollServer finds that i/o on FD should be possible
// again, it will send fd on fd.cr/fd.cw to wake any waiting processes.
// This protocol is implemented as s.WaitRead() and s.WaitWrite().
//
// There is one subtlety: when sending on s.cr/s.cw, the
// poll server is probably in a system call, waiting for an fd
// to become ready. It's not looking at the request channels.
// To resolve this, the poll server waits not just on the FDs it has
// been given but also its own pipe. After sending on the
// buffered channel s.cr/s.cw, WaitRead/WaitWrite writes a
// byte to the pipe, causing the pollServer's poll system call to
// return. In response to the pipe being readable, the pollServer
// re-polls its request channels.
//
// Note that the ordering is "send request" and then "wake up server".
// If the operations were reversed, there would be a race: the poll
// server might wake up and look at the request channel, see that it
// was empty, and go back to sleep, all before the requester managed
// to send the request. Because the send must complete before the wakeup,
// the request channel must be buffered. A buffer of size 1 is sufficient
// for any request load. If many processes are trying to submit requests,
// one will succeed, the pollServer will read the request, and then the
// channel will be empty for the next process's request. A larger buffer
// might help batch requests.
//
// To avoid races in closing, all fd operations are locked and
// refcounted. when netFD.Close() is called, it calls syscall.Shutdown
// and sets a closing flag. Only when the last reference is removed
// will the fd be closed.
祝你好运重新实现网络。最后的好消息是,您的 zmq-socket 在 go 中将是线程安全的。
fd
为您希望等待的每个人生成一个新的 goroutine ,让他们fd
在阅读内容时将其发送到频道,然后在频道上进行选择。
Go 中的所有 net.Conn 都可以同时访问。如果您的库基于此,则应该没有任何问题。
否则,在 Go 中每个连接生成一个 goroutine 是很常见的。这个 goroutine 通常只负责从套接字读取(可能会阻塞)并使用通道将所有数据转发到另一个协调器 goroutine。然后,该协调器 goroutine 能够使用 select 语句与所有这些套接字连接(包装在通道中)和其他事件进行交互。另请注意,您可以轻松地向这些通道添加缓冲区,以便慢速客户端无法阻止您的协调器 goroutine。