假设我们有一个 Java NIO Selector
,它在多个SocketChannels
读取操作上选择超时:
Selector selector = Selector.open();
channel1.register(selector, SelectionKey.OP_READ);
channel2.register(selector, SelectionKey.OP_READ);
channel3.register(selector, SelectionKey.OP_READ);
channel4.register(selector, SelectionKey.OP_READ);
// ... maybe even more ...
while (true) {
if (selector.select(TIMEOUT) > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isValid() && key.isReadable())
read(key);
iterator.remove();
}
} else {
// All channels timed-out! Cancel and close them all
cancelAndCloseAll(selector.keys());
}
}
如果通道空闲了特定时间,我们需要取消并关闭它,这就是我们使用该selector.select(TIMEOUT)
方法的原因。
但是,如果我们有一些非常活跃的频道,这将不起作用。那些活动通道永远不会让select
to 超时,而所有其他通道可能是空闲的......
一个天真的解决方案如下(也提到here):
SelectionKey
使用该key.attach(object)
方法将活动的最后时间附加到每个通道。每次选择成功后,更新所有就绪键的活动时间。然后遍历所有键,找到空闲超过特定阈值的键。
这可能非常低效,因为活动通道会导致select
非常频繁地触发并且每次都在整个键集上迭代。
那么有没有更好(更有效)的方法来解决这个问题?