2016-03-08 23 views
5

Selector.select(タイムアウト)は、Javadocによればタイムアウト

0を返すこれは、少なくとも一つのチャンネルが選択された後にのみ、このセレクタのwakeupメソッドが呼び出され、現在のスレッドが中断され、または所定の戻りタイムアウト期間のいずれか早い方が終了します。

時折、それはこれらの4つの場合のいずれなし返し:チャンネルが選択さ

  1. 少なくとも一つの:それは0
  2. ウェイクアップ方法を返すが呼び出される:wakeup
  3. と呼ばれていません
  4. 現在のスレッドが中断されました:Thread.interrupted()がfalseを返します
  5. 与えられたタイムアウト期間が期限が切れる: https://github.com/xqbase/tuna/blob/debug/core/src/main/java/com/xqbase/tuna/ConnectorImpl.java

    :私はいくつかのログを追加しライン392及びライン402で私の元には

    2016年3月15日に更新ログ

に応じて期限が切れていませんここで

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) { ... 

はログです:

... 
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 
... 

これは非常に奇妙です。選択されたキーはなく、中断はなく、タイムアウトもウェイクアップもしませんでしたが、返されました。

Javaにバグはありますか?私のJavaバージョンは1.8.0_51-b16(64ビットサーバVM)で、CentOS 6.5 x64 linodeで動作します。

+0

いいえよく書かれた質問+1 –

+0

このプロセスから外部プロセスを起動しますか? –

+0

@RoeeShenberg no。ただ一つのJavaプロセスであり、他のプロセスを呼び出さない。 – auntyellow

答えて

1

これは本当にJDKのバグかもしれません。 NettyとMinaもこのような問題に遭遇し、セレクタを回避策として再構築するようです。

が最新ネッティーコード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; 
      } 

参照ミナ2.0コード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(); 
        } 
       } 

をそのため、新しいセレクタを登録すること(ベストプラクティスかもしれ)は予期しない結果を返しますゼロ

2

Javadocはかなり明確です。

各選択操作中に、セレクタの選択キーセットにキーを追加したり削除したりすることができます。基礎となるオペレーティングシステムは次のように更新するために照会さ

  1. ...

  2. :選択)は(選択によって行われる、(長い)を選択し、selectNow()メソッド、および3つのステップを含むされます選択操作が開始された瞬間に、そのキーの関心セットによって識別される操作のいずれかを実行するために、各残りのチャネルの準備が整ったことを示す。少なくとも一つのそのような操作のための準備ができているチャンネルは、次の2つのいずれかのアクションが実行された場合:チャネルのキーがセット選択されたキーになっていない場合は

    1. それは、そのセットに追加され、その準備動作セットは、チャネルが準備完了と報告された動作を正確に識別するように変更される。準備完了セットに以前に記録された準備情報は破棄されます。

    2. これ以外の場合、チャネルのキーはすでに選択キーセットに入っているため、チャネルの準備ができたと報告された新しい操作を識別するために、その準備完了セットが変更されます。レディーセットに以前に記録されたレディネス情報は保存されます。言い換えれば、基礎となるシステムによって返されたレディーセットは、キーの現在のレディーセットにビットごとに分離されます。何が起こっている

それはゼロを返し、その選択の上で、すでに選択されたキーセットで選択キーWASS、準備キーの数ではそれほど変化は起こりませんでした。

戻り値::select(int timeout)方法(私の強調)のためのセクションでも

  • 可能操作セットが更新されたキーの数、おそらくゼロ、
+0

私のプログラムでは、一度選択されると、選択されたすべてのキーが処理され、クリアされます。だから私は次の選択はタイムアウトしない限りゼロを返すべきではないと思う。 – auntyellow

+0

これをチェックする方法は、予期しないゼロリターンが発生した場合、すべてのレディーキーを繰り返し、レディー操作を調べてそこにあるものを確認することです。すべての準備作業をどのように処理したのですか? –

+0

ありがとうございます。試してみるよ... – auntyellow

関連する問題