メインスレッドがSelector
とSelectionKeys
を使用して多数の接続を処理するアプリケーションを作成しています。私は、タスクをワーカースレッドに渡そうとするときに、競合状態で何らかの問題に遭遇しました。SelectorとSelectionKeysを使用してスレッドプールに委任する
私のメインループは次のようになります。
selector = Selector.open(); //Create selector
serverSocketChannel = ServerSocketChannel.open(); //Create socket channel, configure blocking, and bind
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(PORT));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); //Register channel to selector
ByteBuffer buffer = ByteBuffer.allocate(8000);
while(true){
selector.select();
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while(iterator.hasNext()){
SelectionKey key = iterator.next();
if(key.isAcceptable()){
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
}
if(key.isReadable()){
taskList.add(new ReadTask(key));
}
if(key.isWritable()){
}
iterator.remove();
}
}
ここでの考え方は、クライアントがサーバーにデータを送信しようとすると、それはOP_READの関心を持つキーを受信し、そのキーでタスクを作成することですので、スレッドプールはメインスレッドをブロックしないように読み込みを処理できます。
問題このループを呼び出すと、ワーカースレッドに渡されるキーの処理中に継続され、taskList.add(new ReadTask(key));
が呼び出され、最終的が呼び出されたときの間の全体の時間は、メインスレッドがまだ反復されると見ているということですキーはまだ選択されています。キーのチャネル上で読み込みが呼び出された後、キーは非アクティブとしてマークされ、クライアントからの別の正当な書き込みによってキーが再度アクティブ化されるまでセレクタによって選択されないように見えます。
キーをマークして、セレクタが選択したキーのリストに読み込みを行わずに戻さないようにする方法はありますか?私はselector.selectedKeys.remove(key)
を試しましたが、これはConcurrentModification例外を生成します。
ワーカースレッドはすでに開始されています。これらのタスクは同期キューを介して渡されます。あなたが推奨しているのは、OP_READを別のOP(OP_WRITEなど)に変更することです。 – 0x6