Selector.select 没有按预期阻塞

发布于 2024-12-02 13:38:54 字数 3026 浏览 0 评论 0原文

在我当前的项目中,我注意到 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

妄断弥空 2024-12-09 13:38:54

OP_CONNECT 触发并且 finishConnect() 返回“true”之前,不要注册 OP_READ。此时您必须取消注册OP_CONNECT

同样,在有东西要写之前,不要为 OP_WRITE 注册通道。 OP_WRITE 始终处于就绪状态,除非套接字发送缓冲区已满,因此仅应在检测到该条件后注册它 (write()返回零),并且您应该立即取消注册它(除非该情况再次发生)。

最后,OP_CONNECTOP_WRITE 在底层是相同的,鉴于我刚才所说的 OP_WRITE 解释了选择器的旋转。

Don't register OP_READ until OP_CONNECT has fired and finishConnect() has returned 'true'. At that point you must deregister OP_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 and OP_WRITE are the same thing under the hood, which given what I've just said about OP_WRITE explains your selector spins.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文