可以在Go中同时等待两个通道和文件描述符吗?

时间:2012-07-05 10:51:24

标签: go channel

我知道我可以在Go中使用select {}语法在多个通道上等待,并使用syscall.Select()或类似函数等待多个文件描述符。但是有可能一次在两个频道上等待吗?

对于后台,我希望有一个goroutine接受通过通道的消息并通过套接字连接(由gozmq提供)转发它们,同时等待套接字连接上的回复。

由于底层库的线程安全要求,套接字一次只能在一个线程中访问,这就是为什么我想知道是否有办法从单个goroutine处理这个。

3 个答案:

答案 0 :(得分:10)

无法选择通道和文件描述符,因为抽象处于不同的级别。通道由操作系统的go运行时和文件描述符处理。您需要的是在它们之间架起桥梁,这可以通过net.Pipe()完成。

您需要做的就是将一个goroutine专用于epoll() / select()来观看您的zmq套接字以及单个“唤醒”net.Pipe()。这是您的投票服务器。另一个goroutine会监听您的读写通道。当有人发送读取或写入通道时,第二个goroutine将发送管道以唤醒轮询服务器。

这是标准库中的网络包的工作方式。我强烈建议您阅读它以获取灵感(或窃取...... BSD许可证非常宽松)。

以下是来自net本身的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中的线程安全。

答案 1 :(得分:2)

为您希望等待的每个fd生成一个新的goroutine,让他们在阅读内容时将fd发送到频道,在频道上选择。

答案 2 :(得分:1)

Go中的所有net.Conn都可以同时访问。如果您的图书馆以此为基础,那么应该没有任何问题。

否则,Go在每个连接中生成一个goroutine是很常见的。这个goroutine通常只负责从套接字读取(可能会阻塞)并使用通道将所有数据转发给另一个协调器goroutine。然后,该协调器goroutine能够使用select语句与所有这些套接字连接(包含在通道中)和其他事件进行交互。另请注意,您可以轻松地向这些频道添加缓冲区,以便慢速客户端无法阻止您的协调器goroutine。