Как определить селектор.вызов пробуждения

StackOverflow https://stackoverflow.com/questions/333593

  •  22-07-2019
  •  | 
  •  

Вопрос

Если бы я написал:

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.

правильно ли он определит сигнал пробуждения?

Справочная информация:

Я пишу сервер, который большую часть времени просто принимает пакеты и сохраняет их в файле.Очень редко у приложения возникает необходимость отправить самому себе специальный пакет.Для этого он инициирует соединение (из другого потока) с серверным сокетом:

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

Проблема в том, что SelectableChannel.register() может блокироваться, если основной поток уже находится в Selector.select().Чтобы этого не произошло, я вызываю Selector.wakeup(), который позволяет основному потоку преждевременно вернуться из select().Чтобы убедиться, что у другого потока есть возможность завершить вызов register, мне пришлось бы синхронизировать основной поток, но мне пришлось бы сделать это после каждый возврат из функции select().Если бы я мог определить, вернулось ли оно из select () из-за вызова wakeup (), то я мог бы оптимизировать его именно для этого случая.

Итак, теоретически верхний фрагмент кода должен работать, но мне было интересно, будет ли он делать только это, потому что он полагается на какое-то неопределенное поведение?

Спасибо за любые подсказки.

Это было полезно?

Решение

Я бы предположил, что предлагаемый фрагмент в принципе вообще не будет работать в соответствии с контрактами Selector#select() и Selector#selectedKeys().От Селектор:

  • Набор выбранных ключей - это набор ключей таким образом, что было обнаружено, что канал каждого ключа готов по меньшей мере к одной из операций, определенных в интересующем наборе ключей, во время предыдущей операции выбора.Этот набор возвращается методом selectedKeys.
public abstract int select(long timeout)
                throws IOException
    Returns:
        The number of keys, possibly zero, whose ready-operation sets were
        updated

Когда я это прочитал, размер selectedKeys set всегда должен быть равен числу, возвращаемому select по определению.Я заметил - как, возможно, и вы, - что некоторые реализации не совсем соответствуют документации, и на самом деле selectedKeys возвращает все ключи с обновленными наборами готовых операций, даже если они не были обновлены во время вызова select.Единственный другой индикатор того, что select проснулся из-за вызова wakeup возможно, количество ключей равно нулю;однако любой из этих методов был бы в лучшем случае ненадежным.

Обычный способ справиться с этим, как подразумевается, с помощью управления параллелизмом.Я бы не стал беспокоиться о времени выполнения здесь;это классический пример преждевременная оптимизация.

Если вас действительно не беспокоят допуски в одну цифру в микросекунду, вы не заметите никакого замедления, а если вас беспокоит этот уровень допуска, Selector в любом случае, это будет недостаточно надежно для вас.

Вот пример обычного механизма для этого, использующего 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();
    }
}

Другие советы

Я не понимаю, почему ваш код вообще должен работать.

Почему бы просто не проверить volatile после select?

Если select() возвращает ноль, либо время ожидания истекло, либо он был разбужен.

Вы не можете быть действительно уверены, что единственная причина, по которой селектор проснулся, была связана с тревожным звонком.У вас также может быть активность сокета.

Итак, вам нужно заставить вызывающего wakeup также выполнить что-то вроде установки изменчивого логического значения, чтобы указать на его желание привлечь внимание.Цикл выбора может проверять значение этого логического значения каждый раз, когда он просыпается.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top