3

スケーラブルなサーバーを作成するために、NIO とマルチスレッドの読み取り処理を組み合わせようとしています。Netty や MINA などのフレームワークは使用できません。低レベルのクライアント/サーバー プロトコルの詳細がいくつかあるため、どちらにも実装するには多大な労力がかかります。コードを調べていたところ、次のスニペットに潜在的な競合状態があることに気付きました。

//executes in selector thread
public void runSelector() {
    //...
    Set<SelectionKey> keys = selector.selectedKeys();
    for (Iterator<SelectionKey> keyIter = keys.iterator(); keyIter.hasNext(); ) {
        final SelectionKey key = keyIter.next();
        keyIter.remove();
        if (key.isValid() && key.isReadable()) { //point A
            //maybe some other short calculations
            ((SocketChannel) key.channel()).read(buffer); //point B
            workerThreadPool.submit(new Runnable() { public void run() { processRead(key, buffer); } });
        }
    }
    //...
}

//executes in a worker thread
private void processRead(SelectionKey key, ByteBuffer buf) {
    //... somewhere
    key.cancel();
    //...
}

これは非常にありそうもないイベントですが、runSelector() メソッドでコメントした 2 つのポイントの間にセレクター スレッドがあるときに、ワーカー スレッドで key.cancel() を呼び出す可能性は十分にあります。これは同時実行性の高いマシンに展開される可能性があり、セレクター スレッドを実行する CPU コアが停止する可能性があることに注意してください。セレクタ スレッドの key.isReadable() と channel.read() の間のワーカー スレッドで key.cancel() が呼び出され、CancelledKeyException が発生する可能性があるという懸念は妥当ですか? runSelector() が反復の最後にそれらすべてをキャンセルできるように、キャンセルされるすべてのキーを格納するスレッドセーフなコレクションが必要ですか? Netty や MINA などのより専門的なプロジェクトは、このようなケースをどのように処理しますか?

4

1 に答える 1

2

リソースをロックし、

synchronized(key) {
    ((SocketChannel) key.channel()).read(buffer); //point B
}

これが奇妙に見える場合は、並行性に関するこのOracle チュートリアルを見てください。

于 2012-06-20T16:17:08.467 に答える