2008-12-02 69 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()返回零,要麼超時或它被喚醒。