C ++ 20 में कोरआउट क्या हैं?


104

कोरटाइन में क्या हैं ?

किन तरीकों से यह "Parallelism2" या / और "Concurrency2" (छवि के नीचे देखें) से अलग है?

नीचे की छवि ISOCPP की है।

https://isocpp.org/files/img/wg21-timeline-2017-03.png

यहां छवि विवरण दर्ज करें


3
उत्तर देने के लिए "में किस तरह से की अवधारणा है coroutines से अलग समानांतरवाद और संगामिति ?" - en.wikipedia.org/wiki/Coroutine
Ben Voigt


3
कॉरटीन के लिए एक बहुत ही अच्छा और आसानी से पालन किया जाने वाला परिचय है जेम्स मैकनेलिस की प्रस्तुति "सी ++ कॉरआउट्स का परिचय" (Cppcon2016)।
फीलसुमरू

2
अंत में यह कवर करना भी अच्छा होगा कि " C ++ में कोरआउट कैसे होते हैं ? अन्य भाषाओं के कोरआउट और फिर से शुरू होने वाले कार्यों के कार्यान्वयन से अलग हैं?" (जो ऊपर से जुड़ा हुआ विकिपीडिया लेख है, भाषा अज्ञेय है, पता नहीं है)
बेन वोइग्ट

1
और कौन इस "संगरोध को C ++ 20" में पढ़ेगा?
साहिब यार

जवाबों:


200

एक अमूर्त स्तर पर, Coroutines ने निष्पादन की स्थिति होने के विचार से एक निष्पादन राज्य होने के विचार को विभाजित कर दिया।

SIMD (एकल निर्देश एकाधिक डेटा) में कई "निष्पादन के धागे" होते हैं, लेकिन केवल एक निष्पादन स्थिति होती है (यह सिर्फ कई डेटा पर काम करता है)। संभवतः समानांतर एल्गोरिदम इस तरह से एक सा है, जिसमें आपके पास अलग-अलग डेटा पर एक "प्रोग्राम" चलता है।

थ्रेडिंग में कई "निष्पादन के धागे" और कई निष्पादन राज्य हैं। आपके पास एक से अधिक कार्यक्रम हैं, और निष्पादन के एक से अधिक धागे हैं।

Coroutines में कई निष्पादन स्थितियां हैं, लेकिन निष्पादन का एक धागा नहीं है। आपके पास एक कार्यक्रम है, और कार्यक्रम में राज्य है, लेकिन इसमें निष्पादन का कोई धागा नहीं है।


कोरटाइन्स का सबसे आसान उदाहरण अन्य भाषाओं के जनरेटर या गणनाकर्ता हैं।

छद्म कोड में:

function Generator() {
  for (i = 0 to 100)
    produce i
}

Generatorकहा जाता है, और पहली बार यह कहा जाता है देता है 0। इसकी स्थिति को याद किया जाता है (कोरटाइन्स के कार्यान्वयन के साथ कितना राज्य बदलता है), और अगली बार जब आप इसे कहते हैं तो यह जारी रहता है जहां इसे छोड़ा गया था। तो यह अगली बार 1 लौटाता है। फिर २।

अंत में यह लूप के अंत तक पहुंचता है और फ़ंक्शन के अंत में गिर जाता है; कोरटाइन समाप्त हो गया है। (यहां जो होता है, वह भाषा के आधार पर भिन्न होता है, जिसके बारे में हम बात कर रहे हैं; अजगर में, यह एक अपवाद फेंकता है)।

Coroutines C ++ में यह क्षमता लाती है।

दो प्रकार के कोरआउट हैं; स्टैकफुल और स्टैकलेस।

एक स्टैकलेस कोरआउट केवल स्थानीय चर को अपने राज्य और निष्पादन के स्थान पर संग्रहीत करता है।

एक स्टैकफुल कोरटाइन एक पूरे स्टैक (एक धागे की तरह) को स्टोर करता है।

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

एक मूल्य के उत्पादन की प्रक्रिया को "उपज" कहा जाता है, क्योंकि कोरटाइन सहकारी बहुपरत की तरह थोड़े होते हैं; आप कॉल करने वाले को वापस निष्पादन का बिंदु दे रहे हैं।

बूस्ट में स्टैकफुल कोरटाइन का कार्यान्वयन है; यह आपको आपके लिए एक फ़ंक्शन को कॉल करने देता है। स्टैकफुल कोरआउट अधिक शक्तिशाली होते हैं, लेकिन अधिक महंगे भी होते हैं।


एक साधारण जनरेटर की तुलना में कोराउटीन के लिए अधिक है। आप एक coroutine में एक coroutine की प्रतीक्षा कर सकते हैं, जो आपको एक उपयोगी तरीके से coroutines बनाने की सुविधा देता है।

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


C ++ में Coroutines का विशिष्ट कार्यान्वयन थोड़ा दिलचस्प है।

अपने सबसे बुनियादी स्तर पर, यह कुछ कीवर्ड को C ++ में जोड़ता है co_return co_await co_yield, साथ में कुछ पुस्तकालय प्रकार जो उनके साथ काम करते हैं।

एक फ़ंक्शन उसके शरीर में से एक होने से एक कोरटाइन बन जाता है। इसलिए उनकी घोषणा से वे कार्यों से अप्रभेद्य हैं।

जब उन तीन खोजशब्दों में से एक का उपयोग फ़ंक्शन बॉडी में किया जाता है, तो रिटर्न प्रकार और तर्कों की कुछ मानक अनिवार्य जांच होती है और फ़ंक्शन को कोरटाइन में बदल दिया जाता है। यह परीक्षा कंपाइलर को बताती है कि फंक्शन सस्पेंड होने पर फ़ंक्शन स्टेट को कहाँ स्टोर करना है।

सबसे सरल कोरआउट एक जनरेटर है:

generator<int> get_integers( int start=0, int step=1 ) {
  for (int current=start; true; current+= step)
    co_yield current;
}

co_yieldकार्यों के निष्पादन को निलंबित कर देता है, उस स्थिति को संग्रहीत करता है generator<int>, फिर के currentमाध्यम से मान लौटाता है generator<int>

आप लौटे पूर्णांकों पर लूप कर सकते हैं।

co_awaitइस बीच आप एक दूसरे पर एक coroutine विभाजन की सुविधा देता है। यदि आप एक कॉरआउट में हैं और आपको प्रगति करने से पहले एक प्रतीक्षा योग्य चीज़ (अक्सर एक कॉरआउट) के परिणामों की आवश्यकता है, तो आप उस co_awaitपर। यदि वे तैयार हैं, तो आप तुरंत आगे बढ़ें; यदि नहीं, तो जब तक आप इंतजार कर रहे हैं, तब तक आप तैयार नहीं हैं।

std::future<std::expected<std::string>> load_data( std::string resource )
{
  auto handle = co_await open_resouce(resource);
  while( auto line = co_await read_line(handle)) {
    if (std::optional<std::string> r = parse_data_from_line( line ))
       co_return *r;
  }
  co_return std::unexpected( resource_lacks_data(resource) );
}

load_dataएक कोरटाइन है जो std::futureनामित संसाधन को खोलने पर उत्पन्न होता है और हम उस बिंदु पर पार्स करने का प्रबंधन करते हैं जहां हमने डेटा का अनुरोध किया था।

open_resourceऔर read_lineशायद एसिंक्स कॉरआउट हैं जो एक फ़ाइल खोलते हैं और उससे लाइनें पढ़ते हैं। co_awaitके निलंबित और तैयार राज्य जोड़ता है load_dataउनकी प्रगति के लिए।

सी ++ कोरटाइन इससे कहीं अधिक लचीले हैं, क्योंकि उन्हें उपयोगकर्ता-अंतरिक्ष प्रकारों के शीर्ष पर भाषा सुविधाओं के न्यूनतम सेट के रूप में लागू किया गया था। उपयोगकर्ता-अंतरिक्ष प्रकार प्रभावी रूप से क्या co_return co_awaitऔर क्या co_yield अर्थ निर्धारित करते हैं - मैंने देखा है कि लोग इसका उपयोग करने के लिए विचित्र वैकल्पिक अभिव्यक्ति को लागू करते हैं, जैसे कि एक co_awaitखाली वैकल्पिक स्वचालित रूप से खाली राज्य को बाहरी वैकल्पिक के लिए प्रचारित करता है:

modified_optional<int> add( modified_optional<int> a, modified_optional<int> b ) {
  return (co_await a) + (co_await b);
}

के बजाय

std::optional<int> add( std::optional<int> a, std::optional<int> b ) {
  if (!a) return std::nullopt;
  if (!b) return std::nullopt;
  return *a + *b;
}

26
यह कोरटाइन के स्पष्ट स्पष्टीकरणों में से एक है जो मैंने कभी पढ़ा है। उन्हें तुलना करना और उन्हें SIMD और शास्त्रीय धागों से अलग करना एक उत्कृष्ट विचार था।
सर्वव्यापी

2
मैं ऐड-ऑप्शंस के उदाहरण को नहीं समझता। std :: वैकल्पिक <int> एक प्रतीक्षित वस्तु नहीं है।
जिव डडसन

1
@ मोर्ड हां यह 1 तत्व लौटाने वाला है। चमकाने की आवश्यकता हो सकती है; अगर हम चाहते हैं कि एक से अधिक लाइन को एक अलग नियंत्रण प्रवाह की आवश्यकता हो।
यक - एडम नेवरामोंट

1
@ माफ करना, होना चाहिए था ;;
यक्क - एडम नेवरामॉन्ट

1
@LF ऐसे सरल कार्य के लिए शायद कोई अंतर नहीं है। लेकिन सामान्य रूप से मैं जो अंतर देखता हूं, वह यह है कि एक कोरटाइन अपने शरीर में प्रवेश / निकास (निष्पादन) बिंदु को याद करता है जबकि एक स्थिर कार्य हर बार शुरुआत से ही निष्पादन शुरू कर देता है। "स्थानीय" डेटा का स्थान मेरे हिसाब से अप्रासंगिक है।
avp

21

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

Microsoft से पिछले प्रायोगिक कोरूटाइन कार्यान्वयन ने कॉपी किए गए स्टैक का उपयोग किया था ताकि आप गहरी नेस्टेड फ़ंक्शन से भी वापस आ सकें। लेकिन इस संस्करण को C ++ समिति द्वारा अस्वीकार कर दिया गया था। आप इसे बूस्ट फाइबर लाइब्रेरी के साथ उदाहरण के लिए लागू कर सकते हैं।


1

कोराउटीन को माना जाता है (C ++ में) ऐसे कार्य जो किसी अन्य रूटीन को पूरा करने के लिए "प्रतीक्षा" करने में सक्षम होते हैं और निलंबित, रुके हुए, प्रतीक्षा के लिए, रूटीन को पूरा करने के लिए जो भी आवश्यक हो, प्रदान करते हैं। C ++ लोगों के लिए सबसे दिलचस्प है कि यह सुविधा है कि coroutines आदर्श रूप से कोई स्टैक स्पेस नहीं लेगा ... C # पहले से ही कुछ इस तरह से इंतजार कर रहा है और उपज प्राप्त कर सकता है लेकिन इसे प्राप्त करने के लिए C ++ को फिर से बनाना पड़ सकता है।

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

समानतावाद संगोष्ठी की तरह लगता है और इसमें शामिल हो सकता है, लेकिन वास्तव में एक भौतिक निर्माण है जिसमें सॉफ्टवेयर के साथ अधिक या कम समानांतर फैशन में व्यवस्थित कई प्रोसेसर शामिल होते हैं जो कोड को अलग-अलग भागों में निर्देशित करने में सक्षम होते हैं जहां इसे चलाया जाएगा और परिणाम वापस प्राप्त होंगे तुल्यकालिक।


9
चिंता की चिंता और अलगाव पूरी तरह से असंबंधित हैं। निलंबित दिनचर्या के लिए जानकारी प्रदान करने के लिए कोराटाइन्स नहीं हैं, वे फिर से शुरू करने योग्य दिनचर्या हैं।
बेन वोइग्ट
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.