2012-07-03 5 views
6

私は、Java NIOを使って、スレッドとスレッドを選択して(実際の読み書きを行うために)、単純なファイルサーバを実装しています。Java NIO SocketChannel.read()with multithread

コードの主要部分は、次のようになります

while (true) { 
    int num = selector.select(); 
    if (num > 0) { 
     Iterator<SelectionKey> keys = selector.selectedKeys().iterator(); 
     final SelectionKey key = keys.next(); 
     keys.remove(); 

     if (key.isValid()) { 
      if (key.isAcceptable()) { 
       accept(key); 
      } else if (key.isReadable()) { 
       performReadInWorkerThread (key); 
      } else if (key.isWritable()) { 
       performWriteInWorkerThread (key); 
      } 
     } 
    } 
} 

あなたは、読み出し/書き込み可能なチャネルが選択されたコードスニペットから見ることができるように、私は選択のスレッドからの書き込み/読み取りをオフロードワーカースレッドに送信します。

ここで問題は、readbleチャンネルがワーカースレッドに渡され、チャンネルからの読み込みを終了する前に、選択スレッドが再びループし、selector.select()がまだ選択されている読み取り可能なチャンネルを選択することです以前に割り当てられたワーカースレッドによってまだ完全に消費されていないチャネルの入力バッファ)、再びチャネルが別のワーカースレッドに引き渡され、ワーカースレッドが同じチャネルを読み取るとなります。

これは設計上の問題だと思います。私の質問は、同時に1つのスレッドだけがチャンネルを読むことを保証する方法です。

+0

あなたが助けてくれるma回答の読んだ部分を参照してください。 – Amith

+0

単純なファイルサーバーが必要な場合は、ブロックするNIOを使用するか、IOをブロックします。 IMHOこれはセレクタを使いこなすよりもずっと簡単です。 –

答えて

6

なぜですか?読み取りはブロックされません。現在のスレッドで行います。あなたはこのように無限の問題のためだけに入っています。読み込みスレッドに渡す前にOP_READの登録を解除する必要がありますが、これは簡単ですが、読み込みスレッドが読み込みを完了すると、OP_READを再登録する必要があります。これには、(i)セレクタ(ii)保留中の再登録の待ち行列を使用して、そのチャネル上の次の読み出しを次回のセレクタの後まで遅延させるwakeup()。これは、おそらく何もしない場合に選択スレッドを実行させ、無駄である目覚め、また無駄です。そうでなければ、すぐにセレクタを待ち行列に追加する必要があります。何も準備ができていなければ無駄です。さまざまな選択スレッドと読取りスレッドを使用している説得力のあるNIOアーキテクチャを見たことはありません。

これを行わないでください。マルチスレッドを使用する必要がある場合は、チャネルをグループ化して、それぞれに独自のセレクタと独自のスレッドを設定し、それらすべてのスレッドに独自の読み取りを行わせます。

同様に、別のスレッドで書き込む必要はありません。何か書いたことがあるときに書いてください。

+0

ありがとうございます。つまり、スレッドが1つだけ必要なのですか?何千ものクライアントが大容量のファイルを要求していると言えば、私のサーバはこれらの要求を同時に1つのスレッドでどのように処理するのですか? – neevek

+0

Neevek、私はEJPが読み取り手順だけを開示したと思います。彼の答えは非常に合理的です。あなたは単一のスレッドで読み込みますが、ワーカースレッドに切り替えてリクエストデータを解析します。 大きなファイルを扱う - これらのファイルが静的であれば、nginxに切り替えるほうが良いですが、javaを処理するのではなく、zerocopyを探すことができます:http://www.ibm.com/developerworks/library/j-zerocopy/ – user486075

+0

@Neevek EJPはritです。あなたの接続が受け入れられると、各クライアントは別々のスレッドとして動作する必要があります。 – Amith

-1

SelectionKey.cancel()の方法を確認してください。

+0

私は 'キープアライブ'接続が必要です。オープンされたソケットを使用して複数のリクエストを処理するので、SelectionKeyをキャンセルしないでください。 '未来のチャンネルから読み込む '必要があります。 – neevek

+0

それはとにかくどこかで問題を動かすだけです。私の答えを見てください。 – EJP

1

NIOの場合、1つの原則を念頭に置いてください。 メインの選択スレッドで読み書きを行います。 この原則は、ハードウェアの性質を反映したものです。 メインの選択スレッドの読み込みが迅速でないことを心配しないでください。現代のサーバーでは、CPUは常にネットワークカードより高速です。したがって、1つのスレッドでのノーブロッキング読み取りも、ネットワークカード操作より高速です。 1つのスレッドはすでにパケットを読み取るのに十分です。これ以上スレッドは必要ありません。