2013-05-16 8 views
5

मेरे पास ऐसे कर्मचारी हैं जो गणना करने के लिए तत्वों की कतार साझा करते हैं। प्रत्येक पुनरावृत्ति पर, प्रत्येक कार्यकर्ता कतार से एक तत्व को हटा देता है और गणना के लिए अधिक तत्व उत्पन्न कर सकता है, जिसे एक ही कतार में रखा जा रहा है। असल में, प्रत्येक निर्माता भी एक उपभोक्ता है। कतार में कोई तत्व नहीं होने पर गणना समाप्त होती है और सभी श्रमिकों ने वर्तमान तत्व की गणना समाप्त कर दी है (इसलिए गणना करने के लिए कोई और तत्व नहीं बनाया जा सकता है)। मैं एक प्रेषक/समन्वयक से बचना चाहता हूं, इसलिए श्रमिकों को समन्वय करना चाहिए। कार्यकर्ता को यह पता लगाने की अनुमति देने के लिए सबसे अच्छा तरीका क्या है कि रोकथाम की स्थिति मान्य है, और इसलिए दूसरों की तरफ से गणना को रोकें?जावा उत्पादक-उपभोक्ता को रोकने की स्थिति

उदाहरण के लिए, यदि सभी धागे सिर्फ इस आते-जाते हैं, जब तत्वों सभी गणना, यह सभी धागे सदा अवरुद्ध किया जा रहा परिणाम होगा:

while (true) { 
    element = queue.poll(); 
    newElements[] = compute(element); 
    if (newElements.length > 0) { 
     queue.addAll(newElements); 
    } 
} 

उत्तर

6

सक्रिय धागे की गिनती को बनाए रखें।

public class ThreadCounter { 
    public static final AtomicInteger threadCounter = new AtomicInteger(N); 
    public static final AtomicInteger queueCounter = new AtomicInteger(0); 
    public static final Object poisonPill = new Object(); 
    public static volatile boolean cancel = false; // or use a final AomticBoolean instead 
} 

आपका धागे 'मतदान पाश की तरह निम्नलिखित (मैं यह सोचते कर रहा हूँ कि तुम एक BlockingQueue उपयोग कर रहे हैं)

while(!ThreadCounter.cancel) { 
    int threadCount = ThreadCounter.threadCounter.decrementAndGet(); // decrement before blocking 
    if(threadCount == 0 && ThreadCounter.queueCounter.get() == 0) { 
     ThreadCounter.cancel = true; 
     queue.offer(ThreadCounter.poisonPill); 
    } else { 
     Object obj = queue.take(); 
     ThreadCounter.threadCounter.incrementAndGet(); // increment when the thread is no longer blocking 
     ThreadCounter.queueCounter.decrementAndGet(); 
     if(obj == ThreadCounter.poisonPill) { 
      queue.offer(obj); // send the poison pill back through the queue so the other threads can read it 
      continue; 
     } 
    } 
} 

एक धागा बारे में BlockingQueue पर ब्लॉक करने के लिए तो यह decrements है दिखना चाहिए काउंटर; यदि सभी धागे पहले से ही कतार पर प्रतीक्षा कर रहे हैं (जिसका अर्थ है कि counter == 0), तो अंतिम धागा cancel को सत्य पर सेट करता है, फिर अन्य धागे को जागने के लिए कतार के माध्यम से एक जहर गोली भेजता है; प्रत्येक थ्रेड जहर की गोली को देखता है, शेष धागे को जागने के लिए इसे कतार के माध्यम से वापस भेजता है, और फिर यह देखता है कि cancel सत्य पर सेट है।

संपादित करें: मैं एक queueCounter कि कतार में वस्तुओं की संख्या की गिनती का कहना जोड़कर डेटा दौड़ हटा दिया है (जाहिर है आप भी भी आप जोड़ रहे हैं वस्तुओं के लिए एक queueCounter.incrementAndGet() कॉल जोड़ने की आवश्यकता होगी कतार में)। यह निम्नानुसार काम करता है: यदि threadCount == 0, लेकिन queueCount != 0, तो इसका मतलब है कि थ्रेड ने कतार से एक आइटम हटा दिया है, लेकिन अभी तक threadCount.getAndIncrement नहीं कहा गया है, और इसलिए रद्द चर सत्य पर सेट नहीं है। यह महत्वपूर्ण है कि threadCount.getAndIncrement कॉल queueCount.getAndDecrement कॉल से पहले कॉल करें, अन्यथा आपके पास अभी भी डेटा रेस होगी। इससे कोई फर्क नहीं पड़ता कि आप queueCount.getAndIncrement पर किस ऑर्डर को कॉल करते हैं, क्योंकि आप threadCount.getAndDecrement पर कॉल के साथ इसे अंतःस्थापित नहीं करेंगे (बाद वाले को लूप के अंत में बुलाया जाएगा, पूर्व को लूप की शुरुआत में बुलाया जाएगा)।

ध्यान दें कि प्रक्रिया को समाप्त करने का निर्धारण करने के लिए आप केवल queueCount का उपयोग नहीं कर सकते हैं, क्योंकि थ्रेड अभी भी कतार में कोई भी डेटा रखे बिना सक्रिय हो सकता है - दूसरे शब्दों में, queueCount शून्य होगा, लेकिन होगा एक बार धागे ने अपने वर्तमान पुनरावृत्ति को समाप्त करने के बाद शून्य न हो।

कतार के माध्यम से बार-बार poisonPill भेजने के बजाय, आप कतार के माध्यम से रद्द करने के थ्रेड को भेज सकते हैं (एन -1) poisonPills। अगर आप एक अलग कतार का उपयोग करके इस दृष्टिकोण का उपयोग करते हैं तो सावधान रहें, क्योंकि कुछ कतार (जैसे अमेज़ॅन की सरल कतार सेवा) अपने take विधियों के बराबर कई आइटम लौटा सकती है, इस मामले में आपको बार-बार poisonPill को सुनिश्चित करने की आवश्यकता होगी कि सब कुछ बंद हो जाता है।

साथ ही, बजाय एक while(!cancel) पाश का उपयोग कर के, आप एक while(true) पाश का उपयोग करें और जब पाश एक poisonPill

+0

मैं अनुमान रद्द अस्थिर होना चाहिए का पता लगाता है तोड़ सकते हैं? – marcorossi

+0

क्या होता है जब कॉल के बीच queue.take() और incrementAndGet(), और अन्य थ्रेड कॉल के बीच होता है तो गिनती == 0 और queue.isEmpty() स्थिति का मूल्यांकन कर रहा है? – marcorossi

+0

@marcorossi हाँ 'रद्द करें' अस्थिर होना चाहिए ताकि परिवर्तन सभी धागे में दिखाई दे। उदाहरण के लिए सभी धागे 'रद्द' फ़ील्ड में सबसे हालिया लेखन देखते हैं, यह पढ़ने और लिखने की गारंटी देता है परमाणु हैं। जवाब संपादित किया है। – adamjmarkham

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