2011-08-30 57 views
4

在我目前的項目中,我注意到select()未按預期阻塞。 即使沒有IO存在,它也不會阻止並始終返回。所以我得到了一個繁忙的CPU。Selector.select不按預期方式阻止

註冊將始終由另一個線程調用,所以我需要鎖定和喚醒。

的醫生說爲selectNow()

調用此方法清除喚醒方法的任何先前調用的效果。

所以我在每次迭代結束時調用該方法。沒有succsess。 我沒有找到任何例子或解釋如何使用selectNow爲我的目的。

代碼有什麼問題?


這是我的示例代碼,所以你可以測試這個。

BTW:另一個stackoverflow問題是我的代碼的rolemodel。 編輯:修正示例!它現在有效。

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"); 
    } 
} 

回答

8

直到OP_CONNECT已經解僱 finishConnect()返回 '真' 不註冊OP_READ。此時您必須取消註冊OP_CONNECT。

同樣地,在您需要寫東西之前,不要註冊OP_WRITE的通道。除非套接字發送緩衝區已滿,否則OP_WRITE始終處於準備就緒狀態,因此只應在之後註冊,並檢測到該條件(write()返回零),並且應該立即註銷它(除非發生此情況)再次)。

最後,OP_CONNECT和OP_WRITE是同一件事情,它給出了我剛剛提到的關於OP_WRITE解釋你的選擇器旋轉的內容。

+0

謝謝。你的提示解決了這個問題。 我改變'handleKey'。 –

+0

+1非常重要的建議,從文檔中丟失。 –