Selector.select 没有按预期阻塞
在我当前的项目中,我注意到 select()
没有按预期阻塞。 它根本不会阻塞并且总是返回,即使不存在 IO 也是如此。所以我的CPU很忙。
注册总是会被另一个线程调用,所以我需要锁和唤醒。
该文档对于 selectNow()
说:
调用此方法会清除之前调用唤醒方法的效果。
所以我在每次迭代结束时调用该方法。没有成功。 我没有找到如何使用 selectNow
来实现我的目的的示例或解释。
代码有什么问题吗?
这是我的示例代码,因此您可以测试一下。
顺便说一句:另一个 stackoverflow 问题是我的代码的角色模型。 编辑:示例已修复!现在可以了。
import java.io.IOException;
import java.net.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantLock;
public class Test implements Runnable {
ReentrantLock selectorLock = new ReentrantLock();
Selector selector;
boolean alive;
@Override
public void run() {
SelectionKey key;
Iterator<SelectionKey> keys;
alive = true;
try {
while (alive) {
selectorLock.lock();
selectorLock.unlock();
selector.select();
System.out.println("select() returned");
keys = selector.selectedKeys().iterator();
// handle each "event"
while (keys.hasNext()) {
key = keys.next();
// mark as handled
keys.remove();
// handle
handleKey(key);
}
//selector.selectNow(); // don't fix this
}
} catch ( IOException e ) {
e.printStackTrace();
}
}
private void handleKey(SelectionKey key)
throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (key.isConnectable()) {
System.out.println("connecting");
if ( channel.finishConnect() ) {
key.interestOps(SelectionKey.OP_READ);
} else {
key.cancel();
}
} else if (key.isReadable()) {
System.out.println("reading");
// read and detect remote close
channel.read(ByteBuffer.allocate(64));
}
}
public void register(SelectableChannel channel, int ops, Object attachment)
throws ClosedChannelException {
selectorLock.lock();
try {
System.out.println("wakeup");
selector.wakeup();
channel.register(selector, ops, attachment);
} finally {
selectorLock.unlock();
}
}
public Test()
throws IOException {
selector = Selector.open();
}
public static void main(String[] args)
throws IOException {
Test t = new Test();
new Thread(t).start();
SocketAddress address = new InetSocketAddress("localhost", 8080);
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(address);
t.register(channel, SelectionKey.OP_CONNECT, "test channel attachment");
}
}
In my current project I notice that select()
do not block as expected.
It do not block at all and return always, even when no IO was present. So I got a busy cpu.
The registration will always invoked by another thread, so I need the lock and the wakeup.
The doc says for selectNow()
:
Invoking this method clears the effect of any previous invocations of the wakeup method.
So I invoke the method at the end of each iteration. no succsess.
I found no example or explanation how to use selectNow
for my purpose.
What is wrong with the code?
Here is my example code, so you can test this.
BTW: Another stackoverflow question was the rolemodel of my code.
EDIT: Example fixed! It works now.
import java.io.IOException;
import java.net.*;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.concurrent.locks.ReentrantLock;
public class Test implements Runnable {
ReentrantLock selectorLock = new ReentrantLock();
Selector selector;
boolean alive;
@Override
public void run() {
SelectionKey key;
Iterator<SelectionKey> keys;
alive = true;
try {
while (alive) {
selectorLock.lock();
selectorLock.unlock();
selector.select();
System.out.println("select() returned");
keys = selector.selectedKeys().iterator();
// handle each "event"
while (keys.hasNext()) {
key = keys.next();
// mark as handled
keys.remove();
// handle
handleKey(key);
}
//selector.selectNow(); // don't fix this
}
} catch ( IOException e ) {
e.printStackTrace();
}
}
private void handleKey(SelectionKey key)
throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
if (key.isConnectable()) {
System.out.println("connecting");
if ( channel.finishConnect() ) {
key.interestOps(SelectionKey.OP_READ);
} else {
key.cancel();
}
} else if (key.isReadable()) {
System.out.println("reading");
// read and detect remote close
channel.read(ByteBuffer.allocate(64));
}
}
public void register(SelectableChannel channel, int ops, Object attachment)
throws ClosedChannelException {
selectorLock.lock();
try {
System.out.println("wakeup");
selector.wakeup();
channel.register(selector, ops, attachment);
} finally {
selectorLock.unlock();
}
}
public Test()
throws IOException {
selector = Selector.open();
}
public static void main(String[] args)
throws IOException {
Test t = new Test();
new Thread(t).start();
SocketAddress address = new InetSocketAddress("localhost", 8080);
SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
channel.connect(address);
t.register(channel, SelectionKey.OP_CONNECT, "test channel attachment");
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
在
OP_CONNECT
触发并且finishConnect()
返回“true”之前,不要注册OP_READ
。此时您必须取消注册OP_CONNECT
。同样,在有东西要写之前,不要为 OP_WRITE 注册通道。
OP_WRITE
始终处于就绪状态,除非套接字发送缓冲区已满,因此仅应在检测到该条件后注册它 (write()
返回零),并且您应该立即取消注册它(除非该情况再次发生)。最后,
OP_CONNECT
和OP_WRITE
在底层是相同的,鉴于我刚才所说的OP_WRITE
解释了选择器的旋转。Don't register
OP_READ
untilOP_CONNECT
has fired andfinishConnect()
has returned 'true'. At that point you must deregisterOP_CONNECT
.Similarly don't register channels for OP_WRITE until you have something to write.
OP_WRITE
is always ready except when the socket send buffer is full, so it should only be registered after you have detected that condition (write()
returns zero), and you should de-register it immediately it fires (unless the condition happens again).And finally
OP_CONNECT
andOP_WRITE
are the same thing under the hood, which given what I've just said aboutOP_WRITE
explains your selector spins.