2008-12-02 47 views
5

如果我会写:如何检测Selector.wakeup呼叫

 
int selectedChannels = selector.select(); 
Set selectedKeys = selector.selectedKeys(); 
if (selectedChannels != selectedKeys.size()) { 
    // Selector.select() returned because of a call to Selector.wakeup() 
    // so do synchronization. 
} 
// Continue with handling selected channels. 

将它正确地检测到唤醒打电话?

Backgroundinformation:

我正在写的大部分时间只接收一个文件包,并将它们存储服务器。很少有应用程序需要发送一个特殊的数据包。为此,它启动(从不同的线程)到服务器套接字连接:

 
SocketChannel channel = SocketChannel.open(); 
channel.configureBlocking(false); 
channel.connect(new InetSocketAddress(InetAddress.getLocalHost(), PORT)); 
selector.wakeup(); 
SelectionKey key = channel.register(selector, SelectionKey.OP_CONNECT); 

的问题是,SelectableChannel.register()如果主线程已经在Selector.select可能会阻塞()。为了防止这种情况发生,我调用Selector.wakeup()让主线程从select()过早返回。为了确保其他线程有机会完成注册调用,我将不得不同步主线程,但是我必须在之后每从select()返回。如果我能够通过唤醒()调用来检测它是否从select()返回,那么我可以针对这种情况优化它。

因此,理论上最好的代码片段应该可以工作,但我想知道它是否会这样做,因为它依赖于某些未指定的行为?

感谢您的任何提示。

+0

你想避免锁的动机是什么?难道你担心执行时间,或者你是否在很多地方调用select(),并希望避免代码重复? – 2008-12-02 14:30:16

+0

我很担心执行时间,尽管select()和register()已经在已注册的键上同步,但这可能是一个争议点。 – BugSlayer 2008-12-02 14:46:11

+0

同意,担心这是过早优化的一个典型例子。除非你真的在谈论单微秒公差,否则你不会注意到任何放缓。我会用锁来回答一个问题。 – 2008-12-02 16:42:35

回答

3

我猜想根据Selector#select()Selector#selectedKeys()的合同,原则上建议的代码段根本不起作用。从Selector

  • 的选择键集是一组,使得每个键的通道中检测的先前的选择操作期间以准备在该键的兴趣组中标识的操作中的至少一个密钥。该集合由selectedKeys方法返回。
public abstract int select(long timeout) 
       throws IOException 
    Returns: 
     The number of keys, possibly zero, whose ready-operation sets were 
     updated 

当我读到的是,selectedKeys集的大小应该总是等于通过定义由select返回的数字。我注意到 - 你也可能会这样 - 有些实现并不完全遵循文档,事实上,selectedKeys会返回带有已更新的就绪操作集的所有密钥,即使它们在调用select期间未更新。由于致电wakeup而选择醒来的唯一的其他指标可能是密钥数量为零;然而,任何一种方法最好都是不可靠的。

正如通常所说的,通过并发控制处理这个问题的常用方法。我不会担心这里的执行时间。这是premature optimization的一个典型例子。

除非您真的担心单位数微秒的容差,否则您将不会注意到任何放缓 - 并且如果您担心该容差水平,那么Selector对您来说不会足够可靠。

下面是通常的机构,用于此的例子,利用ReentrantLock来完成适当的并发:

ReentrantLock selectorGuard; 
Selector selector; 

private void doSelect() { 
    // Don't enter a select if another thread is in a critical block 
    selectorGuard.lock(); 
    selectorGuard.unlock(); 

    selector.select(); 
    Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator(); 

    while(keyIter.hasNext()) { 

     SelectionKey key = keyIter.next(); 
     keyIter.remove(); 

     // Process key 
    } 
} 

private void addToSelector() { 

    // Lock the selector guard to prevent another select until complete 
    selectorGuard.lock(); 

    try { 
     selector.wakeup(); 

     // Do logic that registers channel with selector appropriately 

    } finally { 
     selectorGuard.unlock(); 
    } 
} 
0

我不明白为什么你的代码一般工作。

为什么不在select之后检查volatile

+0

我正在寻找一个'隐式'解决方案,这意味着我想要使用无论如何都没有任何“显式”标记(如布尔变量)的信息。 – BugSlayer 2008-12-02 14:08:01

-1

你不能确定选择器醒来的唯一原因是由于唤醒电话。你也可能有套接字活动。

所以,你需要使唤醒的调用者也做一些事情,比如设置一个易变的布尔值来表示它注意的欲望。每次唤醒时,选择器循环都可以检查该布尔值的值。

0

如果select()返回零,要么超时或它被唤醒。