如何检测Selector.wakeup调用

5 java nio

如果我愿意写:

int selectedChannels = selector.select();
Set selectedKeys = selector.selectedKeys();
if ( selectedChannels != selectedKeys.size() ) {
    // Selector.select() returned because of a call to Selector.wakeup()
    // so do synchronization.
}
// Continue with handling selected channels.

它会正确检测到唤醒呼叫吗?

Backgroundinformation:

我正在编写一个服务器,大多数时候只接收数据包并将它们存储在一个文件中.应用程序很少需要向自己发送特殊数据包.为此,它启动连接(从不同的线程)到服务器套接字:

SocketChannel channel = SocketChannel.open();
channel.configureBlocking( false );
channel.connect( new InetSocketAddress( InetAddress.getLocalHost(), PORT ));
selector.wakeup();
SelectionKey key = channel.register( selector, SelectionKey.OP_CONNECT );

问题是如果主线程已经在Selector.select()中,SelectableChannel.register()可能会阻塞.为了防止这种情况发生,我正在调用Selector.wakeup(),让主线程从select()过早返回.为了确保其他线程有机会完成寄存器调用,我必须同步主线程,但是每次从select()返回后我都必须这样做.如果我可以检测是否因为wakeup()调用而从select()返回,那么我可以针对这种情况对其进行优化.

因此,理论上顶级代码片段应该可以工作,但我想知道它是否只会这样做,因为它依赖于一些未指定的行为?

谢谢你的任何提示.

Gre*_*ase 3

Selector#select()我猜想,根据和的合同,所提议的片段原则上根本不起作用Selector#selectedKeys()。来自选择器

  • 所选键集是这样的键集,使得每个键的通道被检测为准备好用于在之前的选择操作期间在该键的兴趣集中标识的操作中的至少一个。该集合由 selectedKeys 方法返回。
公共抽象int选择(长超时)
                抛出 IOException
    返回:
        其就绪操作集为的键的数量(可能为零)
        更新

正如我读到的那样,集合的大小应始终等于定义selectedKeys返回的数字。select我注意到 - 正如您可能也注意到的那样 - 某些实现并不完全遵循文档,实际上selectedKeys返回带有更新的就绪操作集的所有键,即使它们在调用select. select 由于调用而唤醒的唯一其他指标wakeup可能是键的数量为零;然而,这两种方法充其量都是不可靠的。

正如所暗示的,处理这个问题的通常方法是通过并发控制。我不会担心这里的执行时间;这是过早优化的典型例子。

除非您真的担心个位数微秒的容差,否则您不会注意到任何减速 - 如果您担心这种容差水平,那么 aSelector对您来说无论如何都不够可靠。

以下是常用机制的示例,使用 aReentrantLock来实现适当的并发性:

ReentrantLock selectorGuard;
Selector selector;

private void doSelect() {
    // Don't enter a select if another thread is in a critical block
    selectorGuard.lock();
    selectorGuard.unlock();

    selector.select();
    Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();

    while(keyIter.hasNext()) {

        SelectionKey key = keyIter.next();
        keyIter.remove();

        // Process key
    }
}

private void addToSelector() {

    // Lock the selector guard to prevent another select until complete
    selectorGuard.lock();

    try {
        selector.wakeup();

        // Do logic that registers channel with selector appropriately

    } finally {
        selectorGuard.unlock();
    }
}
Run Code Online (Sandbox Code Playgroud)