7

1つの選択スレッドと複数のワーカースレッド(実際の読み取り/書き込みを実行するため)を備えたJavaNIOを使用して単純なファイルサーバーを実装しています。

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

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);
            }
        }
    }
}

コードスニペットからわかるように、読み取り/書き込み可能なチャネルが選択されている場合、読み取り/書き込みを選択スレッドからワーカースレッドにオフロードします。

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

これは設計上の問題だと思います。私の質問は、1つのスレッドだけが同時にチャネルを読み取るようにするにはどうすればよいですか?

4

2 に答える 2

7

なんで?読み取りはブロックされません。現在のスレッドでそれを行います。あなたはこのように終わりのない問題に直面しています。読み取りスレッドに渡す前にOP_READの登録を解除する必要があります。これは非常に簡単ですが、難しいのは、読み取りスレッドが読み取りを完了したときにOP_READを再登録する必要があることです。これには、(i)セレクターが必要です。 wakeup()は、何もすることがない場合にselectスレッドを実行しますが、これは無駄です。または、(ii)保留中の再登録のキューを使用して、そのチャネルでの次の読み取りを次回セレクターが終了するまで遅らせます。ウェイクアップしますが、これも無駄です。そうしないと、キューに追加したらすぐにセレクターをウェイクアップする必要があります。これも、準備ができていない場合は無駄です。異なる選択スレッドと読み取りスレッドを使用した説得力のあるNIOアーキテクチャを見たことがありません。

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

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

于 2012-07-03T06:38:56.437 に答える
4

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

于 2013-02-05T15:15:06.353 に答える