2015-03-16 27 views
41

मैंने स्केल निष्पादन संदर्भों, अंतर्निहित थ्रेडिंग मॉडल और समरूपता के विषय को सीखने में थोड़ी देर बिताई है। आप किन तरीकों से scala.concurrent.blockingकरता है "क्रम व्यवहार को समायोजित" scaladoc में वर्णित के रूप और "प्रदर्शन में सुधार या गतिरोध से बचने कर सकते हैं" में व्याख्या कर सकते हैं?scala.concurrent.blocking - वास्तव में यह क्या करता है?

the documentation में, इसे एपीआई का इंतजार करने के साधन के रूप में प्रस्तुत किया जाता है जो प्रतीक्षा करने योग्य नहीं है। (शायद लंबे समय तक चलने वाले गणना को लपेटा जाना चाहिए?)।

यह वास्तव में क्या करता है?

Following through the source आसानी से अपने रहस्यों को धोखा नहीं देता है।

उत्तर

60

blockingExecutionContext पर संकेत के रूप में कार्य करने के लिए है कि निहित कोड अवरुद्ध है और थ्रेड भुखमरी का कारण बन सकता है। इससे थ्रेड पूल को भुखमरी को रोकने के लिए नए धागे को उछालने का मौका मिलेगा। "रनटाइम व्यवहार समायोजित करें" का अर्थ यह है। हालांकि यह जादू नहीं है, और हर ExecutionContext के साथ काम नहीं करेगा।

import scala.concurrent._ 
val ec = scala.concurrent.ExecutionContext.Implicits.global 

(0 to 100) foreach { n => 
    Future { 
     println("starting Future: " + n) 
     blocking { Thread.sleep(3000) } 
     println("ending Future: " + n) 
    }(ec) 
} 

यह डिफ़ॉल्ट वैश्विक ExecutionContext उपयोग कर रहा है:

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

अब 4 धागे की एक निश्चित पूल के साथ इस उदाहरण पर नजर:

import java.util.concurrent.Executors 
val executorService = Executors.newFixedThreadPool(4) 
val ec = ExecutionContext.fromExecutorService(executorService) 

(0 to 100) foreach { n => 
    Future { 
     println("starting Future: " + n) 
     blocking { Thread.sleep(3000) } 
     println("ending Future: " + n) 
    }(ec) 
} 

यह ExecutionContext नई धागे को उत्पन्न करने को संभालने के लिए नहीं बनाया गया है, और इसलिए मेरी अवरुद्ध blocking से घिरा कोड के साथ भी, आप देख सकते हैं कि यह अभी भी एक समय में केवल 4 Future एस पर निष्पादित करेगा। और इसलिए हम कहते हैं कि "प्रदर्शन में सुधार कर सकता है या डेडलॉक्स से बच सकता है" - यह गारंटी नहीं है। जैसा कि हम बाद के ExecutionContext में देखते हैं, इसकी गारंटी नहीं है।

यह कैसे काम करता है? के रूप में जुड़ा हुआ है, blocking इस कोड को निष्पादित करता है:

BlockContext.current.blockOn(body)(scala.concurrent.AwaitPermission) 

BlockContext.current को पुन: प्राप्त वर्तमान धागे से BlockContext, देखा hereBlockContext आमतौर पर ThreadBlockContext विशेषता मिश्रित है। स्रोत में देखा गया है, यह या तो ThreadLocal में संग्रहीत है, या यदि यह वहां नहीं मिला है, तो यह वर्तमान धागे से मिलान पैटर्न है। यदि वर्तमान धागा BlockContext नहीं है, तो DefaultBlockContext इसके बजाए उपयोग किया जाता है।

अगला, blockOn वर्तमान BlockContext पर कॉल किया जाता है। blockOnBlockContext में एक सार विधि है, इसलिए यह कार्यान्वयन इस बात पर निर्भर करता है कि ExecutionContext इसे कैसे संभालता है। यदि हम implementation for DefaultBlockContext देखते हैं (जब वर्तमान धागा BlockContext नहीं है), तो हम देखते हैं कि blockOn वास्तव में वहां कुछ भी नहीं करता है।तो गैर-BlockContext में blocking का उपयोग करने का अर्थ है कि कुछ भी विशेष नहीं किया जाता है, और कोड किसी भी दुष्प्रभाव के साथ चलाया जाता है।

BlockContext एस के धागे के बारे में क्या? उदाहरण के लिए, global संदर्भ में, here देखा गया, blockOn काफी कुछ करता है। गहराई से खोदना, आप देख सकते हैं कि यह ForkJoinPool हैड के तहत DefaultThreadFactory के साथ ForkJoinPool में नए धागे को बढ़ाने के लिए उपयोग किए जाने वाले उसी स्निपेट में परिभाषित किया गया है। BlockContext (थ्रेड) से blockOn के कार्यान्वयन के बिना, ForkJoinPool नहीं जानता कि आप अवरुद्ध कर रहे हैं, और प्रतिक्रिया में अधिक धागे को बढ़ाने की कोशिश नहीं करेंगे।

स्कैला का Await भी, इसके कार्यान्वयन के लिए blocking का उपयोग करता है।

+0

ऐसा लगता है कि यह जावा के ['util.concurrent.ForkJoinPool's'] (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ForkJoinPool.ManagedBlocker पर जादू को पूरी तरह से प्रस्तुत कर रहा है) .html) विवेकाधिकार, कुछ बहिष्कृत स्कैला कोड को छोड़कर। मुझे आश्चर्य है, जावा का कांटा कार्यान्वयन में कैसे शामिल होता है और फिर तय करता है कि एक नया धागा कब उठाना है या नहीं। जावा की थ्रेड स्थितियों में एक मानदंड के रूप में थोड़ा [अपर्याप्त] (http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.State.html#BLOCKED) प्रतीत हो सकता है, जब तक कि कोई भी ब्लॉक_ अवरुद्ध नहीं करेगा एक मॉनिटर लॉक शामिल है। – matanster

+0

ForkJoinPool कैसे तय करता है, आपको [स्रोत] (http://docjar.com/html/api/java/util/concurrent/ForkJoinPool.java.html) में उत्तर मिल सकता है जो संतुष्ट होने से थोड़ा कम है। चीजें बहुत कम स्तर प्राप्त करने लगती हैं। 'addWorker'' ForkJoinWorkerTreadFactory' से नए धागे बना सकता है, जिसे दो स्थानों में कहा जाता है: 'सिग्नलवर्क्स' और 'tryPreBlock'। जिनमें से दोनों बिटवाई तर्क के कुछ रोमांचक मात्रा में शामिल हैं। –

+0

हमें इसे एक अलग प्रश्न (या पुस्तक) के लिए छोड़ दें ... अन्यथा दिखाए जाने तक, मुझे लगता है कि अधिकांश अवरुद्ध एपीआई उस जावा 7 कार्यान्वयन के लिए काफी उपयुक्त हो सकता है, एक लंबे सीपीयू गहन कार्य को छोड़कर भी हो सकता है टाइपसेफ "प्रतिक्रियाशील अनुप्रयोग" दृश्य में "अवरुद्ध" माना जाता है। – matanster

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