Селектор Java NIO select() возвращает 0, хотя каналы готовы

Мой Java NIO Selector реализован с использованием select() , поэтому он блокируется до тех пор, пока не произойдет одно из следующих действий:

  1. зарегистрированный канал готов
  2. это wakeup()'ed
  3. поток прерван

Исходя из этого, я сделал несколько предположений о случае, когда select()возвращает 0:

  • это должна быть причина 2. или 3.
  • selectedKeys()должен возвращать пустой ResultSet
  • мне не нужно вызывать selectedKeys()и может перейти к следующей итерации цикла, где select()будет вызван снова

Однако я сталкивался с ситуациями, когда select()возвращал 0, хотя канал был готов. selectedKeys()возвращает Setс 1 SelectionKey, как и ожидалось.

Даже несколько вызовов select()всегда возвращали 0, пока канал не был обработан и SelectionKeyне был удален. Эта ситуация в основном заканчивается бесконечным циклом, поскольку select()не блокируется, но всегда мгновенно возвращает 0.

Упрощенный код:

Selector selector = Selector.open();

SocketChannel channel;

for (...) { // for each node
  // Create and connect channels...
  ...

  channel.configureBlocking(false);
  channel.register(selector, SelectionKey.OP_READ, someRelatedObject);
}

int ready;
Set readyKeys;
while (true) {
  ready = selector.select();
  readyKeys = selector.selectedKeys();

  System.out.println("Ready channels: " + ready);
  System.out.println("Selected channels: " + readyKeys.size());

  if (ready == 0) {
    continue;
  }

  for (SelectionKey key : readyKeys) {
    if (key.isValid() && key.isReadable()) {
      // Take action...
    }

    readyKeys.remove(key);
  }
}

Почему select()возвращает 0 хотя есть готовый канал? Каков предлагаемый способ борьбы с этим?

РЕДАКТИРОВАТЬ:

Изменение этого:

  for (SelectionKey key : readyKeys) {
    if (key.isValid() && key.isReadable()) {
      // Take action...
    }

    readyKeys.remove(key);
  }

на это

  for (SelectionKey key : readyKeys) {
    readyKeys.remove(key);

    if (key.isValid() && key.isReadable()) {
      // Take action...
    }
  }

решило проблему. В некоторых случаях код продолжалцикл forперед remove()вводом ключа.

РЕДАКТИРОВАТЬ 2:

Недавно я узнал, что мой цикл foreachпо выбранному набору ключей неисправен. foreachиспользует итератор набора. Изменение коллекции напрямую (не через методы итератора) во время итерации по ней может привести к «произвольному, недетерминированному» поведению.

Выбранный набор ключей может предоставлять отказоустойчивыйитератор. Fail-fastитераторы обнаруживают такие модификации и выдают исключение ConcurrentModificationExceptionпри следующей итерации. Таким образом, изменение набора в foreachлибо рискует недетерминированным поведением, либо может вызвать исключения — в зависимости от реализации итератора.

Решение: не используйте foreach. Используйте итератор и удалите ключ через iterator.remove().

Iterator iterator;
SelectionKey key;
while (true) {
  // ...

  iterator = selector.selectedKeys().iterator();
  while (iterator.hasNext()) {
    key = iterator.next();
    iterator.remove();
    // ...
  }
}

9
задан riha 30 November 2012 в 23:14
поделиться