2012-03-30 6 views
8

मेरे जावा NIO चयनकर्ता इनमें से किसी भी जब तक select() तो यह ब्लॉकों का उपयोग कर कार्यान्वित किया जाता है कर रहे हैं के होते हैं:जावा NIO चयनकर्ता का चयन करें() 0 देता है हालांकि चैनल तैयार

  1. एक पंजीकृत चैनल तैयार
  2. है यह wakeup() है ' एड
  3. धागा

इस से बाधित है, मैं मामले के बारे में कुछ मान्यताओं बनाया जहां select() रिटर्न 0:

  • यह कारण 2. या 3. गया है चाहिए
  • selectedKeys() लौट जाना एक खाली ResultSet
  • मैं अगले पाश यात्रा जहां select() बुलाया जाएगा करने के लिए जारी रख सकते हैं selectedKeys() कॉल करने के लिए की जरूरत नहीं है और फिर

हालांकि, मुझे ऐसी परिस्थितियों का सामना करना पड़ा जहां select() लौटाया गया था हालांकि एक तैयार चैनल है। selectedKeys()Set को 1 SelectionKey के साथ अपेक्षित के रूप में देता है।

select() तक कई कॉल भी चैनल को संसाधित होने तक 0 लौटाएंगे और SelectionKey हटा दिया गया था। इस स्थिति में मूल रूप से select() के रूप में एक अंतहीन लूप ब्लॉक नहीं करता में समाप्त होता है, लेकिन हमेशा तुरन्त 0.

सरलीकृत कोड वापस:

Selector selector = Selector.open(); 

SocketChannel channel; 

for (...) { // for each node 
    // Create and connect channels... 
    ... 

    channel.configureBlocking(false); 
    channel.register(selector, SelectionKey.OP_READ, someRelatedObject); 
} 

int ready; 
Set<SelectionKey> readyKeys; 
while (true) { 
    ready = selector.select(); 
    readyKeys = selector.selectedKeys(); 

    System.out.println("Ready channels: " + ready); 
    System.out.println("Selected channels: " + readyKeys.size()); 

    if (ready == 0) { 
    continue; 
    } 

    for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 
} 

क्यों select() वापसी 0 यद्यपि वहाँ एक तैयार चैनल है? इससे निपटने का सुझाया गया तरीका क्या है?

संपादित करें:

इस बदलना:

for (SelectionKey key : readyKeys) { 
    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 

    readyKeys.remove(key); 
    } 

इस

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

    if (key.isValid() && key.isReadable()) { 
     // Take action... 
    } 
    } 

को समस्या हल हो। कुछ मामलों में, कोड लूप remove() कुंजी से पहले लूप होगा।

संपादित करें 2:

मैं अभी हाल ही में पता चला कि मेरी foreach समूह चुनने चाबियाँ पर पाश बुरा है। foreach सेट के इटरेटर का उपयोग करता है। एक संग्रह को सीधे संशोधित करना (इटरेटर के तरीकों के माध्यम से नहीं) जबकि इसे फिर से चालू करने के परिणामस्वरूप "मनमाने ढंग से, अनिश्चित" व्यवहार हो सकता है।

चयनित कुंजी सेट असफल-तेज इटरेटर प्रदान कर सकता है। विफल-तेज इटरेटर इस तरह के संशोधनों का पता लगाते हैं और अगले पुनरावृत्ति पर ConcurrentModificationException फेंक देते हैं।इसलिए फोरैच में सेट को संशोधित करना या तो अपर्याप्त व्यवहार को जोखिम देता है या अपवाद का कारण बन सकता है - इटरेटर कार्यान्वयन के आधार पर।

समाधान: foreach प्रयोग नहीं करते। इटरेटर का प्रयोग करें और iterator.remove() के माध्यम से कुंजी को हटा दें।

Iterator<SelectionKey> iterator; 
SelectionKey key; 
while (true) { 
    // ... 

    iterator = selector.selectedKeys().iterator(); 
    while (iterator.hasNext()) { 
    key = iterator.next(); 
    iterator.remove(); 
    // ... 
    } 
} 

उत्तर

7

select() चाबियाँ कि बदल दिया है की संख्या देता है। तो यदि select() कॉल से पहले एक कुंजी पहले से तैयार हो गई थी तो यह 0 लौटा सकता है लेकिन selectedKeys खाली नहीं हो सकता है।

+2

संभवतः यदि कोई कुंजी तैयार थी और हटाया नहीं गया था? – riha

1

जैसा कि आपने नोट किया है, ऐसा इसलिए है क्योंकि आपने चयनित सेट से चयनित कुंजी को नहीं हटाया है।

हम आम तौर पर सेट संसाधित करने के बाद सभी चयनित कुंजी निकालना चाहते हैं के रूप में, आप सिर्फ इतना है कि सेट पर clear() अपने पाश के बाद कॉल कर सकते हैं, एक foreach या पाश में किसी भी प्रकार आप चाहते हैं हो सकता है जो:

Set<SelectionKey> readyKeys = selector.selectedKeys(); 
for (SelectionKey k : readyKeys) { 
    // Process k 
} 
readyKeys.clear(); 

इस तरह, आप किसी भी प्रकार के लूप का उपयोग कर सकते हैं ("नियमित" for या इटरेटर) और सुनिश्चित करें कि for (समस्याग्रस्त continue सहित) के अंदर आप क्या करते हैं, सभी तैयार-कुंजी हटा दी जाती हैं। iterator.remove() आंतरिक रूप से क्या करता है, इस पर निर्भर करता है कि clear() को iterator.remove() के बजाय एक बार कॉल करना अधिक सक्षम हो सकता है (हालांकि यह संभवतः माइक्रो-ऑप्टिमाइज़ेशन होगा)।

+0

प्रश्न का उत्तर नहीं देता है: "क्यों चयन करें()' वापसी 0 हालांकि एक तैयार चैनल है? " – EJP

+0

@EJP आप सही हैं, मैंने इसे सही किया है। क्या यह आपके डाउनवोट को हटाने के लिए पर्याप्त होगा? – Matthieu

+0

'iterator.remove()' के माध्यम से प्रत्येक एकल 'चयनकी' को तत्काल हटाने पर क्या लाभ है? – riha

संबंधित मुद्दे