यह वास्तव में फिर से प्रवेश नहीं है ; आप एक ही धागे (या अलग-अलग थ्रेड्स) में दो बार फ़ंक्शन नहीं चला रहे हैं । आप प्राप्त कर सकते हैं कि पुनरावृत्ति के माध्यम से या कॉलबैक फ़ंक्शन-पॉइंटर के रूप में वर्तमान फ़ंक्शन के पते को किसी अन्य फ़ंक्शन में पास कर सकते हैं। (और यह असुरक्षित नहीं होगा क्योंकि यह तुल्यकालिक होगा)।
यह सिर्फ़ एक सिग्नल हैंडलर और मुख्य धागे के बीच सादा वेनिला डेटा-रेस UB (अनफाइंड बिहेवियर) है: केवल sig_atomic_t
इसके लिए सुरक्षित होने की गारंटी है । दूसरों को काम करने के लिए हो सकता है, जैसे आपके मामले में जहां एक 8-बाइट ऑब्जेक्ट को x86-64 पर एक निर्देश के साथ लोड या संग्रहीत किया जा सकता है, और कंपाइलर उस asm को चुनने के लिए होता है। (@ इकारस के जवाब से पता चलता है)।
MCU प्रोग्रामिंग देखें - लूप करते समय C ++ O2 ऑप्टिमाइज़ेशन टूट जाता है - सिंगल-कोर माइक्रोकंट्रोलर पर एक रुकावट हैंडलर मूल रूप से सिंगल थ्रेडेड प्रोग्राम में सिग्नल हैंडलर के समान होता है। उस मामले में यूबी का परिणाम है कि एक लोड लूप से बाहर फहराया गया।
डेटा-रेस UB की वजह से वास्तव में हो रही छेड़छाड़ के आपके टेस्ट-केस को 32-बिट मोड में विकसित किया गया / परीक्षण किया गया, या एक पुराने डम्बर कंपाइलर के साथ जो अलग-अलग सदस्यों को लोड करता था।
आपके मामले में, कंपाइलर दुकानों को अनंत लूप से बाहर निकाल सकता है क्योंकि कोई भी यूबी-फ्री प्रोग्राम कभी भी उनका निरीक्षण नहीं कर सकता है। data
है _Atomic
या नहींvolatile
, और लूप में कोई अन्य दुष्प्रभाव नहीं हैं। इसलिए कोई रास्ता नहीं है कि कोई भी पाठक इस लेखक के साथ समन्वय कर सके। यह वास्तव में तब होता है जब आप अनुकूलन सक्षम के साथ संकलित करते हैं ( Godbolt मुख्य के नीचे एक खाली लूप दिखाता है)। मैंने संरचना को दो में भी बदल दिया long long
, और gcc movdqa
लूप से पहले एक सिंगल -बाइट स्टोर का उपयोग करता है । (यह परमाणु की गारंटी नहीं है , लेकिन यह लगभग सभी सीपीयू पर प्रचलन में है, यह मानते हुए कि यह संरेखित है, या इंटेल पर केवल कैश-लाइन सीमा पार नहीं करता है। पूर्णांक असाइनमेंट x86 पर स्वाभाविक रूप से संरेखित चर परमाणु पर क्यों है? )
इसलिए सक्षम किए गए अनुकूलन के साथ संकलन करना भी आपके परीक्षण को तोड़ देगा, और आपको हर बार समान मूल्य दिखाएगा। C एक पोर्टेबल असेंबली भाषा नहीं है।
volatile struct two_int
कंपाइलर को मजबूर नहीं करेगा कि वह उन्हें ऑप्टिमाइज़ करे, लेकिन इसे पूरे स्ट्रक्चर को एटमॉइकल लोड / स्टोर करने के लिए मजबूर नहीं करेगा । (यह नहीं रोक या तो ऐसा करने से यह, हालांकि।) नोट है कि volatile
है नहीं डेटा-दौड़ यूबी से बचने, लेकिन व्यवहार में यह अंतर-धागा संचार के लिए पर्याप्त है और कैसे लोगों को (इनलाइन एएसएम के साथ) हाथ से लुढ़का एटोमिक्स बनाया गया था सामान्य CPU आर्किटेक्चर के लिए C11 / C ++ 11 से पहले। वे कैश-सुसंगत इतना volatile
है अभ्यास ज्यादातर के लिए इसी तरह के _Atomic
साथmemory_order_relaxed
शुद्ध लोड और शुद्ध दुकान के लिए, यदि प्रकार के लिए इस्तेमाल किया पर्याप्त है कि संकलक एक एकल अनुदेश का उपयोग करेगा ताकि आप फाड़ नहीं मिलता है संकीर्ण। और निश्चित रूप सेvolatile
आईएसओ सी मानक बनाम लेखन कोड से किसी भी गारंटी देता है कि एक ही एएसएम को compiles का उपयोग कर की जरूरत नहीं है _Atomic
और mo_relaxed।
यदि आपके पास एक फ़ंक्शन था global_var++;
जो एक int
या long long
उस पर था जो आप सिग्नल हैंडलर से मुख्य और अतुल्यकालिक रूप से चलाते हैं , तो यह डेटा-रेस यूबी बनाने के लिए पुन: प्रवेश का उपयोग करने का एक तरीका होगा।
यह कैसे संकलित किया जाता है (स्मृति गंतव्य inc या जोड़ने के लिए, या लोड / inc / store को अलग करने के लिए) यह परमाणु या उसी धागे में संकेत संचालकों के संबंध में नहीं होगा। देखें Can संख्या 'पूर्णांक संख्या' के लिए परमाणु हो ++? x86 पर और C ++ में परमाणु के बारे में अधिक जानकारी के लिए। (C11 की विशेषता stdatomic.h
और _Atomic
विशेषता C ++ 11 के std::atomic<T>
टेम्पलेट के बराबर कार्यक्षमता प्रदान करती है )
एक व्यवधान या अन्य अपवाद एक निर्देश के बीच में नहीं हो सकता है, इसलिए एक मेमोरी-डेस्ट ऐड परमाणु परमाणु है। संदर्भ एक-कोर सीपीयू पर स्विच करता है। केवल (कैश सुसंगत) डीएमए लेखक एकल-कोर सीपीयू पर एक उपसर्ग के add [mem], 1
बिना एक वेतन वृद्धि "कदम" कर सकता है lock
। वहाँ कोई अन्य कोर नहीं है कि एक और धागा चल सकता है।
तो यह संकेतों के मामले के समान है: सिग्नल को संभालने वाले थ्रेड के सामान्य निष्पादन के बजाय एक सिग्नल हैंडलर चलता है , इसलिए इसे एक निर्देश के बीच में नहीं संभाला जा सकता है।