2016-03-08 9 views
5

Nach Javadoc-,Selector.select (timeout) 0 zurück, bevor Timeout

Es gibt nur, nachdem mindestens ein Kanal ausgewählt ist, ist diese Methode des Wakeup-Selektor aufgerufen wird, der aktuelle Thread unterbrochen ist, oder die gegebene Timeout-Zeit läuft ab, je nachdem, was zuerst eintritt.

Aber gelegentlich gibt sie ohne diese 4 Fälle:

  1. mindestens einen Kanal ausgewählt ist: es wird 0 zurückgegeben
  2. Wakeup Methode wird aufgerufen: wakeup nicht
  3. genannt
  4. der aktuelle Thread ist unterbrochen: Thread.interrupted() gibt false
  5. gegeben Timeout-Zeit abgelaufen ist: nicht nach logs abgelaufen

2016-03-15

In meiner Quelle in Zeile 392 und Zeile 402 ich einige Protokolle AKTUALISIERT hinzugefügt: https://github.com/xqbase/tuna/blob/debug/core/src/main/java/com/xqbase/tuna/ConnectorImpl.java

Hier
public boolean doEvents(long timeout) { 
    Log.v("Before Select: " + timeout); 
    int keySize; 
    try { 
     keySize = timeout == 0 ? selector.selectNow() : 
       timeout < 0 ? selector.select() : selector.select(timeout); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
    Set<SelectionKey> selectedKeys = selector.selectedKeys(); 
    if (keySize == 0) { 
     Log.v("After Select(0): selectedKeys=" + selectedKeys.size() + ", " + 
       "interrupt=" + Thread.interrupted()); 
     invokeQueue(); 
     return false; 
    } 

    for (SelectionKey key : selectedKeys) { 
     ... 

ist das Protokoll:

... 
2016-03-15 23:07:49.695 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: Before Select: 8120 
2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: After Select(0): selectedKeys=0, interrupt=false 
2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: Before Select: 8119 
2016-03-15 23:07:49.696 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: After Select(0): selectedKeys=0, interrupt=false 
2016-03-15 23:07:49.700 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: Before Select: 8115 
2016-03-15 23:07:49.701 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: After Select(0): selectedKeys=0, interrupt=false 
2016-03-15 23:07:49.701 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: Before Select: 8114 
2016-03-15 23:07:49.702 com.xqbase.tuna.ConnectorImpl doEvents 
FINE: After Select(0): selectedKeys=0, interrupt=false 
... 

Das ist sehr seltsam: keine ausgewählten Tasten, keine Unterbrechung, kein Timeout und kein Aufwecken, aber es kehrte zurück.

Gibt es einen Bug in Java? Meine Java-Version ist 1.8.0_51-b16 (64-Bit-Server-VM) und wird auf einem CentOS 6.5 x64-Lincode ausgeführt.

+0

Gut geschriebene Frage +1 –

+0

Starten Sie externe Prozesse von diesem Prozess? –

+0

@RoeShenberg Nr. nur ein einziger Java-Prozess und nicht andere Prozesse aufrufen. – auntyellow

Antwort

1

Dies könnte wirklich ein Fehler in JDK sein. Es scheint, dass Netty und Mina auch auf ein solches Problem stoßen und sie den Selektor als Workaround neu aufbauen.

Siehe neueste Netty Code https://github.com/netty/netty/blob/4.1/transport/src/main/java/io/netty/channel/nio/NioEventLoop.java L641-681:

  if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) { 
       // - Selected something, 
       // - waken up by user, or 
       // - the task queue has a pending task. 
       // - a scheduled task is ready for processing 
       break; 
      } 
      ... 
      } else if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && 
        selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) { 
       // The selector returned prematurely many times in a row. 
       // Rebuild the selector to work around the problem. 
       logger.warn(
         "Selector.select() returned prematurely {} times in a row; rebuilding selector.", 
         selectCnt); 

       rebuildSelector(); 
       selector = this.selector; 

       // Select again to populate selectedKeys. 
       selector.selectNow(); 
       selectCnt = 1; 
       break; 
      } 

See Mina 2.0-Code https://github.com/apache/mina/blob/2.0/mina-core/src/main/java/org/apache/mina/core/polling/AbstractPollingIoProcessor.java L1070-1092:

   if (!wakeupCalled.getAndSet(false) && (selected == 0) && (delta < 100)) { 
        // Last chance : the select() may have been 
        // interrupted because we have had an closed channel. 
        if (isBrokenConnection()) { 
         LOG.warn("Broken connection"); 
        } else { 
         LOG.warn("Create a new selector. Selected is 0, delta = " + (t1 - t0)); 
         // Ok, we are hit by the nasty epoll 
         // spinning. 
         // Basically, there is a race condition 
         // which causes a closing file descriptor not to be 
         // considered as available as a selected channel, 
         // but 
         // it stopped the select. The next time we will 
         // call select(), it will exit immediately for the 
         // same 
         // reason, and do so forever, consuming 100% 
         // CPU. 
         // We have to destroy the selector, and 
         // register all the socket on a new one. 
         registerNewSelector(); 
        } 
       } 

daher einen neuen Wähler registrieren, wenn wählen Sie die beste Praxis sein kann () gibt eine unerwartete Null zurück.

2

Die Javadoc ist ziemlich klar.

Während jeder Auswahloperation können Schlüssel zum ausgewählten Tastensatz eines Selektors hinzugefügt oder daraus entfernt werden. Die Auswahl erfolgt durch die select(), wählen Sie (lang) und selectNow() Methoden durchgeführt und umfasst drei Schritte:

  1. ...

  2. Das zugrunde liegende Betriebssystem für ein Update abgefragt wird als die Bereitschaft jedes verbleibenden Kanals, irgendeine der Operationen auszuführen, die durch das Interesse des Schlüssels identifiziert wurden, das zu dem Zeitpunkt gesetzt wurde, zu dem die Auswahloperation begonnen hat. Für einen Kanal, der für mindestens eine solche Operation, eine der beiden folgenden Aktionen bereit ist, durchgeführt wird:

    1. Wenn der Schlüssel des Kanals nicht bereits in der gewählten-Schlüssel festgelegt ist, dann wird es zu diesem Satz hinzugefügt und sein Ready-Operation-Set wird modifiziert, um genau jene Operationen zu identifizieren, für die der Kanal nun als bereit gemeldet wird. Jede Bereitschaftsinformation, die zuvor in dem Bereit-Satz aufgezeichnet wurde, wird verworfen.

    2. Sonst befindet sich der Schlüssel des Kanals bereits im ausgewählten Schlüsselsatz, daher wird sein Betriebsbereitschaftssatz geändert, um alle neuen Vorgänge zu identifizieren, für die der Kanal als bereit gemeldet wird. Jede Bereitschaft, die zuvor im Bereitschaftssatz aufgezeichnet wurde, bleibt erhalten; Mit anderen Worten, die vom zugrunde liegenden System zurückgegebene Bereitmenge wird bitweise in die aktuelle Bereitschaftsmenge des Schlüssels aufgeteilt.

Was ist, dass auf der Auswahl geschieht, die Null, die Auswahltaste wass bereits in der gewählten Schlüsselsatz zurückgibt, so dass keine Änderung in der Anzahl der fertig Schlüssel aufgetreten.

Beachten Sie auch im Abschnitt für die select(int timeout) Methode (Hervorhebung von mir):

Returns:

  • Die Anzahl der Tasten, möglicherweise Null, deren fertige Betrieb Sätze waren aktualisiert
+0

In meinem Programm, sobald ausgewählt, werden alle ausgewählten Keys behandelt und gelöscht. Also denke ich, dass next select nicht null zurückgeben sollte, außer Timeout. – auntyellow

+0

Die Möglichkeit, dies zu überprüfen, wäre, wenn Sie eine unerwartete Nullrückkehr erhalten, durch alle Ready-Schlüssel zu iterieren und die Ready-Operationen zu untersuchen, um zu sehen, was da ist. Wie sind Sie sicher, dass Sie alle vorbereiteten Operationen durchgeführt haben? –

+0

Danke. Ich werde es versuchen ... – auntyellow