2012-01-12 16 views
6

कोड के निम्नलिखित भाग को लिखने के कुछ कारण क्यों हैं बुरा अभ्यास माना जाता है?लूपिंग के बजाए अवरुद्ध क्यों?

while (someList.isEmpty()) { 
    try { 
     Thread.currentThread().sleep(100); 
    } 
    catch (Exception e) {} 
    } 
    // Do something to the list as soon as some thread adds an element to it. 

मेरे लिए, सोने के लिए एक मनमाना मूल्य उठा अच्छा अभ्यास नहीं है, और मैं इस स्थिति में एक BlockingQueue का प्रयोग करेंगे, लेकिन मैं एक से अधिक कारण वहाँ है अगर जानना चाहते हैं क्यों एक नहीं करना चाहिए ऐसे कोड लिखें।

उत्तर

6

घटना पर कार्य करने से पहले यह 50 मिलीसेकंड की औसत देरी लगाता है, और इसे संभालने के लिए कोई घटना नहीं होने पर यह 10 गुना उठता है। यदि उन चीजों में से कोई भी विशेष रूप से कोई फर्क नहीं पड़ता है, तो यह सिर्फ सुरुचिपूर्ण है।

+0

बस सोच रहा है, यह नहीं है कि प्रतीक्षा कार्य आंतरिक रूप से कैसे काम करते हैं? (स्पष्ट रूप से अपवाद को छोड़कर।) – Mehrdad

+1

@ Mehrdad- आमतौर पर नहीं। आमतौर पर धागे को "प्रतीक्षा कतार" में रखा जाता है और किसी भी प्रोसेसर समय नहीं दिया जाता है। जब कुछ घटना होती है जो उन्हें जागृत करती है, तो उन्हें वापस कतार में रखा जाता है ताकि वे निर्धारित किए जाएंगे। इसका मतलब यह है कि यदि आप किसी भी समय केवल दो या तीन धागे सक्रिय हैं तो प्रदर्शन में किसी भी नुकसान के बिना आपके पास लाखों सोने के थ्रेड हो सकते हैं। – templatetypedef

+0

@templatetypedef: फिर ओएस कैसे पता लगाता है कि थ्रेड को किसी विशेष समय स्लाइस पर जागृत किया जाना चाहिए या नहीं? क्या यह हर बार स्लाइस पर एक लूप में धागे की स्थिति की जांच नहीं करनी चाहिए? – Mehrdad

1

ऐसा करने के कई कारण नहीं हैं। सबसे पहले, जैसा कि आपने ध्यान दिया है, इसका मतलब है कि उस घटना के बीच बड़ी देरी हो सकती है जब एक घटना होती है कि थ्रेड को प्रतिक्रिया देना चाहिए और वास्तविक प्रतिक्रिया समय, क्योंकि थ्रेड सो रहा है। दूसरा, चूंकि किसी भी सिस्टम में केवल इतना अलग प्रोसेसर हैं, यदि आपको प्रोसेसर से महत्वपूर्ण धागे को लात मारना है ताकि वे थ्रेड को किसी अन्य समय सोने के लिए कह सकें, तो आप सिस्टम द्वारा किए गए उपयोगी काम की कुल मात्रा घटाते हैं और सिस्टम के पावर उपयोग को बढ़ाएं (जो फोन या एम्बेडेड डिवाइस जैसे सिस्टम में मायने रखता है)।

0

आप अपनी कक्षा में दौड़ की स्थिति भी पेश करते हैं। यदि आप एक सामान्य सूची के बजाय अवरुद्ध कतार का उपयोग कर रहे थे - तब तक धागा तब तक अवरुद्ध होगा जब तक सूची में कोई नई प्रविष्टि न हो। आपके मामले में एक दूसरा धागा सूची में से एक तत्व डाल सकता है और आपका कार्यकर्ता धागा सो रहा है और आप भी ध्यान नहीं देंगे।

0

अन्य उत्तर करने के लिए जोड़ने के लिए, आप भी एक रेस स्थिति यदि आप कतार से एक से अधिक धागा आइटम निकालना होता है:

  1. कतार खाली है
  2. धागा एक कतार में एक तत्व कहते हैं
  3. धागा बी जांचता है कि कतार खाली है या नहीं; यह
  4. थ्रेड सी जांचता है कि कतार खाली है या नहीं; यह
  5. थ्रेड बी कतार से लिया जाता है; सफलता
  6. धागा सी कतार से लेता है; विफलता

आप (एक synchronized ब्लॉक के भीतर) की जाँच करता है, तो कतार खाली है और, iff नहीं है, यह से एक तत्व लेने atomically द्वारा इस संभाल सकता;

T item; 
while ((item = tryTake(someList)) == null) { 
    try { 
     Thread.currentThread().sleep(100); 
    } 
    catch (InterruptedException e) { 
     // it's almost never a good idea to ignore these; need to handle somehow 
    } 
} 
// Do something with the item 

synchronized private T tryTake(List<? extends T> from) { 
    if (from.isEmpty()) return null; 
    T result = from.remove(0); 
    assert result != null : "list may not contain nulls, which is unfortunate" 
    return result; 
} 

या आप हो सकता है सिर्फ एक BlockingQueue प्रयोग किया है: अब अपनी पाश सिर्फ एक बाल भद्दा लग रहा है।

+0

क्या आपका मतलब है 'निकालें (0) '? मुझे लगता है कि आप पहले तत्व को अनदेखा नहीं करना चाहते हैं। –

+0

erm हाँ, टाइपो, अब फिक्सिंग। – yshavit

+0

आपके विवरण में आप 'कतार' का संदर्भ लेते हैं लेकिन आपके कोड में आप 'सूची' का उपयोग करते हैं। यह भ्रमित हो सकता है। आप 'Queue.remove()' का उपयोग कर सकते हैं; बीटीडब्ल्यू एक लिंक्डलिस्ट भी एक कतार है। –

1

लूप एक उत्कृष्ट उदाहरण है जो नहीं करना है। ;)


Thread.currentThread().sleep(100); 

() currentThread प्राप्त करने के लिए कोई जरूरत नहीं यह एक स्थिर तरीका है के रूप में नहीं है। यह रूप में

Thread.sleep(100); 

catch (Exception e) {} 

यह बहुत बुरा व्यवहार है एक ही है।इतना बुरा मैं सुझाव नहीं दूंगा कि आप इसे उदाहरणों में भी डाल दें, क्योंकि कोई कोड कॉपी कर सकता है। दिए गए अपवाद को प्रिंट करके और पढ़कर इस मंच पर प्रश्नों का एक अच्छा हिस्सा हल किया जाएगा।


You don't need to busy wait here. esp. when you expect to be waiting for such a long time. Busy waiting can make sense if you expect to be waiting a very very short amount of time. e.g. 

// From AtomicInteger 
public final int getAndSet(int newValue) { 
    for (;;) { 
     int current = get(); 
     if (compareAndSet(current, newValue)) 
      return current; 
    } 
} 

आप देख सकते हैं, यह काफी दुर्लभ इस पाश एक बार से चारों ओर अधिक है, और तेजी से कम कई चारों ओर बार जाने की संभावना जाने की जरूरत है कि होना चाहिए। (एक वास्तविक अनुप्रयोग में, माइक्रो-बेंचमार्क के बजाए) यह लूप 10 एनएस जितना छोटा हो सकता है, जो कि लंबी देरी नहीं है।


यह 99 एमएस की प्रतीक्षा कर सकता है। कहें कि निर्माता बाद में 1 एमएस प्रविष्टि जोड़ रहा है, उसने कुछ भी नहीं करने के लिए काफी समय इंतजार किया है।

समाधान सरल और स्पष्ट है।

BlockingQueue<E> queue = 

E e = queue.take(); // blocks until an element is ready. 

सूची/कतार केवल एक ExecutorService उपयोग करने के लिए एक और धागा और धागे और कतारों के प्रबंधन के लिए एक बहुत सरल मॉडल में परिवर्तित करने के लिए जा रहा है है

ExecutorService es = 

final E e = 
es.submit(new Runnable() { 
    public void run() { 
     doSomethingWith(e); 
    } 
}); 

आप देख सकते हैं, तो आप कतार या धागे के साथ सीधे काम करने की ज़रूरत नहीं है। आपको सिर्फ यह कहना होगा कि आप थ्रेड पूल क्या करना चाहते हैं।

0

मैं डेविड, टेम्पप्टाइटीफिफ़ इत्यादि द्वारा दिए गए उत्कृष्ट उत्तरों में सीधे जोड़ नहीं सकता हूं - यदि आप इंटर-थ्रेड कॉमर्स विलंबता और संसाधन-अपशिष्ट से बचना चाहते हैं, तो नींद() लूप के साथ इंटर-थ्रेड कॉमर्स न करें।

पूर्वक्रमिक शेड्यूलिंग/भेजने:

सीपीयू स्तर पर, बीच में आता है महत्वपूर्ण हैं। ओएस तब तक कुछ नहीं करता जब तक कि कोई बाधा उत्पन्न न हो जिससे उसके कोड को दर्ज किया जा सके। ध्यान दें कि, ओएस-टर्म में, इंटरप्ट्स दो स्वादों में आते हैं - 'वास्तविक' हार्डवेयर इंटरप्ट जो ड्राइवरों को चलाने के लिए और 'सॉफ़्टवेयर इंटरप्ट्स' का कारण बनते हैं - ये ओएस सिस्टम पहले से चलने वाले धागे से कॉल करते हैं जो संभावित रूप से चलने वाले धागे के सेट का कारण बन सकते हैं बदलने के लिए। कीप्रेस, माउस-मूवमेंट्स, नेटवर्क कार्ड, डिस्क, पृष्ठ-दोष सभी हार्डवेयर इंटरप्ट उत्पन्न करते हैं। प्रतीक्षा और सिग्नल फ़ंक्शंस, और नींद(), उस दूसरी श्रेणी से संबंधित हैं। जब कोई हार्डवेयर बाधा चालक को चलाने का कारण बनती है, तो ड्राइवर जो भी हार्डवेयर-प्रबंधन करता है उसे करने के लिए डिज़ाइन किया जाता है। यदि चालक को ओएस को सिग्नल करने की आवश्यकता है कि कुछ धागे को चलाने की जरूरत है, (शायद डिस्क बफर अब पूर्ण हो गया है और संसाधित होने की आवश्यकता है), ओएस एक एंट्री तंत्र प्रदान करता है जो चालक सीधे इंटरप्ट- खुद को वापस करो, (महत्वपूर्ण!)।

उपरोक्त उदाहरणों की तरह इंटरप्ट्स थ्रेड बना सकते हैं जो दौड़ने के लिए तैयार इंतजार कर रहे थे और/या एक थ्रेड कर सकते हैं जो प्रतीक्षा स्थिति में प्रवेश कर रहा है। बाधा के कोड को संसाधित करने के बाद, ओएस यह निर्धारित करने के लिए अपने शेड्यूलिंग एल्गोरिदम/एस को लागू करता है कि क्या अंतराल से पहले चल रहे धागे का सेट सेट के समान है जो अब चलाना चाहिए। यदि वे हैं, तो ओएस केवल इंटरप्ट-रिटर्न, यदि नहीं, तो ओएस को चल रहे धागे में से एक या अधिक का प्रयास करना होगा। यदि ओएस को एक सीपीयू कोर पर चल रहे थ्रेड को प्रीडेट करने की आवश्यकता है जो कि इंटरप्ट को संभालने वाला नहीं है, तो उसे उस सीपीयू कोर पर नियंत्रण प्राप्त करना होगा। यह 'असली' हार्डवेयर बाधा के माध्यम से करता है - ओएस इंटर-प्रोसेसर ड्राइवर एक हार्डवेयर सिग्नल सेट करता है जो कि थ्रेड चलाने वाले कोर को हार्ड-बाधित करता है जिसे प्रीमेड किया जाना है।

जब एक थ्रेड जिसे प्रीमेटेड किया जाना है ओएस कोड में प्रवेश करता है, तो ओएस थ्रेड के लिए एक पूर्ण संदर्भ सहेज सकता है।कुछ रजिस्टरों को पहले ही इंटरप्ट प्रविष्टि के माध्यम से थ्रेड के ढेर पर सहेजा जा चुका है और इसलिए थ्रेड के स्टैक-पॉइंटर को सहेजने से उन सभी रजिस्टरों को प्रभावी रूप से 'सेव' कर दिया जाएगा, लेकिन ओएस को सामान्य रूप से और अधिक करने की आवश्यकता होगी, जैसे। कैशों को फ्लश करने की आवश्यकता हो सकती है, एफपीयू राज्य को सहेजने की आवश्यकता हो सकती है और, जिस मामले में नया धागा चलाने के लिए एक अलग प्रक्रिया से संबंधित है, स्मृति प्रबंधन प्रबंधन रजिस्टरों को बाहर निकालना होगा । आम तौर पर, ओएस प्रत्येक थ्रेड स्टैक पर ओएस स्टैक आवश्यकताओं को भरने से बचने के लिए जितनी जल्दी हो सके एक निजी ओएस स्टैक में बाधित थ्रेड स्टैक से स्विच करता है।

एक बार संदर्भ/एस सहेजे/सहेजे जाने के बाद, ओएस नए थ्रेड/एस के लिए विस्तारित संदर्भ/एस में 'स्वैप' कर सकता है जिसे चलाना है। अब, ओएस अंततः नए थ्रेड/एस के लिए स्टैक-पॉइंटर लोड कर सकता है और अपने नए तैयार थ्रेड चलाने के लिए इंटरप्ट-रिटर्न कर सकता है।

ओएस तब कुछ भी नहीं करता है। चलने वाले थ्रेड एक और बाधा, (हार्ड या मुलायम) तक चलते हैं, होता है।

महत्वपूर्ण बिंदु:

1) ओएस गिरी एक बड़ी बाधा-हैंडलर कि बीच में-वापसी के लिए बाधित की तुलना में धागे का एक अलग सेट करने के लिए तय कर सकते हैं के रूप में देखा जाना चाहिए।

2) ओएस किसी भी प्रक्रिया में किसी भी थ्रेड पर नियंत्रण प्राप्त कर सकता है, और इससे रोक सकता है, इससे कोई फर्क नहीं पड़ता कि यह किस स्थिति में है या यह किस कोर पर चल रहा है।

3) प्रीemptive शेड्यूलिंग और प्रेषण सभी सिंक्रनाइज़ेशन इत्यादि उत्पन्न करता है जो इन फ़ोरम पर पोस्ट की गई हैं। बड़ी उछाल थ्रेड-स्तर पर हार्ड इंटरप्ट्स के लिए तेज प्रतिक्रिया है। इसके बिना, आपके पीसी पर चलने वाले सभी उच्च-प्रदर्शन ऐप्स - वीडियो स्ट्रीमिंग, फास्ट नेटवर्क्स इत्यादि लगभग असंभव होंगे।

4) ओएस टाइमर केवल इंटरप्ट्स के एक बड़े सेट में से एक है जो चलने वाले धागे के सेट को बदल सकता है। 'टाइम-स्लाइसिंग', (यूघ - मुझे उस शब्द से नफरत है), तैयार धागे के बीच केवल तब होता है जब कंप्यूटर अधिभारित होता है, यानी। तैयार धागे का सेट उन्हें चलाने के लिए उपलब्ध CPU कोर की संख्या से बड़ा है। यदि ओएस शेड्यूलिंग को समझाने के लिए कोई पाठ लिखने से पहले 'इंटरप्ट्स' से पहले 'टाइम-स्लाइसिंग' का उल्लेख किया गया है, तो यह स्पष्टीकरण से अधिक भ्रम पैदा करने की संभावना है। टाइमर इंटरप्ट केवल 'विशेष' है कि कई सिस्टम कॉल में उनके प्राथमिक फ़ंक्शन का बैक अप लेने के लिए टाइमआउट होते हैं, (ठीक है, नींद के लिए), टाइमआउट प्राथमिक कार्य है :)।

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