2012-10-10 32 views
8

我有一個子類Thread與私人Selector和一個公衆register(SelectableChannel channel, ...)方法,允許其他線程註冊通道到選擇器。NIO Selector:如何正確註冊新頻道,同時選擇

由於在回答here,通道的register()塊選擇的select()/select(long timeout)所以我們需要wakeup()選擇。

我的線程無限期選擇(除非它被中斷),它實際上設法調用通道的register()之前進入下一個選擇。所以我想我使用​​塊的簡單鎖定來確保register()首先發生。

代碼:(不相關的代碼的可讀性刪除)

public class SelectorThread extends Thread { 
    ... 

    public void register(SelectableChannel channel, Attachment attachment) throws IOException { 
    channel.configureBlocking(false); 
    synchronized (this) { // LOCKING OCCURS HERE 
     selector.wakeup(); 
     channel.register(selector, 
         SelectionKey.OP_READ, 
         attachment); 
    } 
    } 

    @Override 
    public void run() { 
    int ready; 
    Set<SelectionKey> readyKeys; 
    while (!isInterrupted()) { 
     synchronized (this) {} // LOCKING OCCURS HERE 

     try { 
     ready = selector.select(5000); 
     } catch (IOException e) { 
     e.printStackTrace(); 
     continue; 
     } 

     if (ready == 0) { 
     continue; 
     } 

     readyKeys = selector.selectedKeys(); 

     for (SelectionKey key : readyKeys) { 
     readyKeys.remove(key); 

     if (!key.isValid()) { 
      continue; 
     } 

     if (key.isReadable()) { 
      ... 
     } 
     } 
    } 
    } 
} 

這個簡單的鎖允許register()到線程繼續下一個選擇循環之前發生。據我測試,這按預期工作。

問題: 這是一個「好」的方式來做到這一點,或者是否有任何嚴重的缺點呢?使用列表或隊列(如建議here)來存儲註冊通道,還是使用更復雜的鎖,如this,會更好嗎?那是什麼優點/缺點?或者有什麼「更好」的方法?

+0

在調用select()的同時向選擇器註冊通道時Java線程塊可能重複。該怎麼辦?](https://stackoverflow.com/questions/1057224/java-thread-blocks-while-registering-channel-with-selector-while-select-is-cal) – Flow

回答

3

我真的很驚訝鎖定採集與空塊不會在編譯時刪除。非常酷,它的作品。我的意思是它的工作原理,它是搶先的,這不是最漂亮的方法,但它的工作原理。它比睡眠更好,因爲它是可預測的,而且由於您使用喚醒呼叫,因此如果純粹依賴於選擇超時,則您將根據需要取得進展,而不是定期更新。

這種方法的主要缺點是,你說的是調用註冊王牌的東西,甚至服務請求。在你的系統中可能是這樣,但通常情況並非如此,我可以說這是一個可能的問題。更具前瞻性思維的小問題是,您鎖定了SelectorThread本身,在這種情況下,它是一個較大的對象。雖然隨着你的擴展,這個鎖並不是很好,但只要其他客戶使用這個類,這個鎖就必須記錄下來並加以考慮。就我個人而言,我會去完成另一個鎖,以避免任何不可預見的未來危害。

就我個人而言,我喜歡排隊技術。他們通過這種方式將角色分配給主線和工作人員。儘管所有類型的控制都發生在主設備上,比如在每次選擇檢查隊列中的更多註冊之後,清除並排除任何讀取任務,處理整個連接設置(斷開連接等)的任何更改......「bs」併發模型似乎很好地接受這個模型,這是一個非常標準的模型。我認爲這不是一件壞事,因爲它使得代碼更簡單,更易於測試,並且更易於閱讀imo。只需要多一點時間寫出來。

雖然我會承認,自從我上次寫這些東西以來,已經有很長的一段時間了,還有其他一些圖書館在那裏照顧排隊。

Grizzly Nio Framework雖然有點老,我上次用它,主要的runloop也不錯。它爲你設置了很多排隊。

Apache Mina類似的,它提供了一個排隊框架。

但我的意思是最終取決於你在做什麼。

  • 它是一個單人項目,只是爲了玩弄框架?
  • 這是一段您希望生活多年的生產代碼嗎?
  • 它是您正在迭代的一段生產代碼嗎?

除非您打算將此作爲您向客戶提供的服務的核心部分,否則我會說您的方法沒問題。從長遠來看,它可能只是維護問題。

+0

對我來說非常好的投入,謝謝。我不認爲註冊勝出的服務,因爲喚醒隻影響等待選擇,而不是循環的其餘部分。至於鎖定,最好是鎖定一個簡單的私人對象嗎? (僅用於鎖定註冊調用的對象)我不希望大量的通道同時註冊,所以我認爲註冊隊列有點太多了。但我喜歡這個想法,並可能在將來實施它。 – riha

+0

RE:選擇和服務,是的,這就是我的意思。就像你會花一個空的週期來處理剛剛發生的註冊。不可怕,但需要額外鎖定並立即處理。在排隊系統中,你通常會有非阻塞隊列,這就減少了對這些鎖的需求。 –

+0

RE:鎖,是的,我會做一個內部鎖只是爲了處理選擇器,個人。但是,這聽起來好像你很高興。 –

4

只要將Selector等視爲不是線程安全的,就像Darron建議的那樣,在同一個線程上執行所有選擇相關操作。

NIO選擇器的併發模型是廢話。我必須把它說出來,因爲每個試圖研究它的人都會浪費大量的時間。最後,結論是,忘記它,它不是爲了同時使用。

2

所有你需要的是在register()之前喚醒(),並且在select循環中,如果'ready'爲零,在繼續之前暫時休眠,給register()一個運行的機會。沒有額外的同步:已經夠糟的了;不要讓它變得更糟。我不是那些註冊,取消,改變興趣操作等的隊列的粉絲:他們只是順序化可以並行完成的事情。

+0

請你詳細說明如何註冊渠道真的可以並行進行嗎?如果註冊只能在同一個線程上完成,那麼這又是如何平行的?此外,你認爲什麼是「短暫睡眠」? 1ms的? 10ms的? 100ms的? – riha

+1

@riha Eh?我沒有說過註冊頻道只能在select()線程中完成。它可以通過我在這裏描述的技術在單獨的線程上完成,以回答您關於該主題的問題。當然,這很清楚?重新睡覺,100毫秒應該足夠了。 – EJP

+0

啊,我讀了[這裏](http://stackoverflow.com/a/2179612/589008),對不起。但是,我們是不是在「同時」註冊多個頻道時遇到由選擇器執行的鎖定?如果是這樣的話,使用隊列的好處在哪裏? – riha