Mi Java NIO selector se implementa utilizando select() por lo que se bloquea hasta que cualquiera de estos síntomas:Java Selector NIO seleccionar() devuelve 0 si bien los canales están dispuestos
- un canal registrado está listo
- es wakeup() ' ed
- el hilo se interrumpe
partir de esto, hice algunas suposiciones sobre el caso en que select()
devuelve 0:
- que debe haber sido la razón o 2. 3.
- selectedKeys() debe devolver un vacío
ResultSet
- No necesito llamar
selectedKeys()
y puede continuar a la siguiente iteración del bucle, donde se llamaráselect()
de nuevo
Sin embargo, encontré situaciones donde select()
devolvió 0 aunque hay un canal listo. selectedKeys()
devuelve un Set
con 1 SelectionKey
como se esperaba.
Incluso varias llamadas a select()
siempre devolverán 0 hasta que se haya procesado el canal y se haya eliminado SelectionKey
. Esta situación básicamente termina en un bucle sin fin como select()
no bloquea al instante, pero siempre devolverá 0.
código simplificado:
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<SelectionKey> 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);
}
}
¿Por qué select()
retorno 0 si bien existe un canal listo? ¿Cuál es la forma sugerida de lidiar con esto?
EDIT:
Cambiar esta:
for (SelectionKey key : readyKeys) {
if (key.isValid() && key.isReadable()) {
// Take action...
}
readyKeys.remove(key);
}
a este
for (SelectionKey key : readyKeys) {
readyKeys.remove(key);
if (key.isValid() && key.isReadable()) {
// Take action...
}
}
resuelto el problema. En algunos casos, el código sería continue
el for
bucle antes de remove()
ing la clave.
EDIT 2:
Sólo recientemente se supo que mi foreach bucle sobre las claves seleccionadas conjunto es malo. foreach usa el iterador del conjunto. Modificar una colección directamente (no a través de los métodos del iterador) mientras se itera sobre ella puede generar un comportamiento "arbitrario, indeterminado".
El conjunto de claves seleccionado puede proporcionar un iterador a prueba de fallas. Fail-fast iteradores detectan tales modificaciones y arrojan un ConcurrentModificationException en la siguiente iteración.Por lo tanto, modificar el conjunto en un foreach tiene el riesgo de comportamiento no determinista o puede causar excepciones, dependiendo de la implementación del iterador.
Solución: no utilice foreach. Use el iterador y elimine la clave a través del iterator.remove().
Iterator<SelectionKey> iterator;
SelectionKey key;
while (true) {
// ...
iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
key = iterator.next();
iterator.remove();
// ...
}
}
Posiblemente si una clave estaba lista y no se eliminó? – riha