简体   繁体   English

如何检测Selector.wakeup调用

[英]How to detect a Selector.wakeup call

If I would write: 如果我愿意写:

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.

would it correctly detect the wakeup-call? 它会正确检测到唤醒呼叫吗?

Backgroundinformation: Backgroundinformation:

I'm writing a server which most of the time just receives packets and stores them in a file. 我正在编写一个服务器,大多数时候只接收数据包并将它们存储在一个文件中。 Very rarely the application has the need to send itself a special packet. 应用程序很少需要向自己发送特殊数据包。 For this it initiates a connection (from a different thread) to the server socket: 为此,它启动连接(从不同的线程)到服务器套接字:

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

The problem is that SelectableChannel.register() might block if the main thread is already in Selector.select(). 问题是如果主线程已经在Selector.select()中,SelectableChannel.register()可能会阻塞。 To prevent this from happening I'm calling Selector.wakeup() which let's the main thread return prematurely from select(). 为了防止这种情况发生,我正在调用Selector.wakeup(),让主线程从select()过早返回。 To make sure the other thread has the chance to complete the register-call, I would have to synchronize the main thread, but I would have to do it after every return from select(). 为了确保其他线程有机会完成寄存器调用,我必须同步主线程,但是每次从select()返回后我都必须这样做。 If I could detect whether it returned from select() because of a wakeup() call, then I could optimize it for just this case. 如果我可以检测是否因为wakeup()调用而从select()返回,那么我可以针对这种情况优化它。

So, in theory the top code snippet should work, but I was wondering whether it would only do so, because it relies on some unspecified behavior? 因此,理论上顶级代码片段应该可以工作,但我想知道它是否只会这样做,因为它依赖于一些未指定的行为?

Thanks for any hints. 谢谢你的任何提示。

I would guess that the proposed snippet would not work at all in principle, per the contracts of Selector#select() and Selector#selectedKeys() . 我猜想根据Selector#select()Selector#selectedKeys()的合同,原则上提议的片段根本不起作用。 From Selector : 来自选择器

  • The selected-key set is the set of keys such that each key's channel was detected to be ready for at least one of the operations identified in the key's interest set during a prior selection operation. 所选择的密钥集是一组密钥,使得检测到每个密钥的信道准备好用于在先前选择操作期间在密钥的兴趣集中识别的至少一个操作。 This set is returned by the selectedKeys method. 这个集由selectedKeys方法返回。
public abstract int select(long timeout)
                throws IOException
    Returns:
        The number of keys, possibly zero, whose ready-operation sets were
        updated

As I read that, the size of the selectedKeys set should always equal the number returned by select by definition. 当我读到它时, selectedKeys集的大小应始终等于select by definition返回的数字。 I have noticed - as you may have as well - that some implementations don't quite follow the documentation, and in fact selectedKeys returns all keys with updated ready-operation sets, even if they were not updated during a call to select . 我注意到 - 你可能也有 - 一些实现并不完全遵循文档,事实上selectedKeys返回所有具有更新的就绪操作集的键,即使它们在调用select期间没有更新。 The only other indicator that the select woke up due to a call to wakeup might be that the number of keys is zero; 由于呼叫wakeup而选择唤醒的唯一其他指示符可能是键的数量为零; however either method would be unreliable, at best. 但是,任何一种方法充其量都是不可靠的。

The usual way to handle this is, as implied, through concurrency control. 正如通过并发控制所暗示的那样,处理此问题的常用方法。 I wouldn't worry about execution time here; 我不担心这里的执行时间; this is a classic example of premature optimization . 这是过早优化的典型例子。

Unless you're really worried about single digit microsecond tolerances, you won't notice any slowdown - and if you are worried about that level of tolerance, a Selector isn't going to be reliable enough for you anyway. 除非您真的担心单位数微秒容差,否则您不会注意到任何减速 - 如果您担心这种容差水平,那么Selector对您来说不够可靠。

Here's an example of the usual mechanism for this, using a ReentrantLock to accomplish the appropriate concurrency: 以下是通常机制的一个示例,使用ReentrantLock来实现适当的并发:

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();
    }
}

I don't understand why your code would work in general. 我不明白为什么你的代码一般会起作用。

Why not just check a volatile after select ? 为什么不在select后检查一个volatile

如果select()返回零,则它会超时或被唤醒。

You can't really be sure that the only reason that the selector woke up was due to the wakeup call. 您无法确定选择器醒来的唯一原因是唤醒呼叫。 You may also have socket activity. 您可能还有套接字活动。

So, you need to make the caller of wakeup also do something like setting a volatile boolean to indicate its desire for attention. 所以,你需要让唤醒的调用者也做一些像设置一个volatile布尔值来表示它需要注意的东西。 The the selector loop can check the value of this boolean every time it wakes up. 选择器循环可以在每次唤醒时检查此布尔值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM