क्या मेरे संकलक ने मेरे अप्रयुक्त स्थिर थ्रेड_लोक क्लास के सदस्य की उपेक्षा की?


10

मैं अपनी कक्षा में कुछ थ्रेड पंजीकरण करना चाहता हूं, इसलिए मैं इस thread_localसुविधा के लिए एक चेक जोड़ने का निर्णय लेता हूं :

#include <iostream>
#include <thread>

class Foo {
 public:
  Foo() {
    std::cout << "Foo()" << std::endl;
  }
  ~Foo() {
    std::cout << "~Foo()" << std::endl;
  }
};

class Bar {
 public:
  Bar() {
    std::cout << "Bar()" << std::endl;
    //foo;
  }
  ~Bar() {
    std::cout << "~Bar()" << std::endl;
  }
 private:
  static thread_local Foo foo;
};

thread_local Foo Bar::foo;

void worker() {
  {
    std::cout << "enter block" << std::endl;
    Bar bar1;
    Bar bar2;
    std::cout << "exit block" << std::endl;
  }
}

int main() {
  std::thread t1(worker);
  std::thread t2(worker);
  t1.join();
  t2.join();
  std::cout << "thread died" << std::endl;
}

कोड सरल है। मेरी Barकक्षा में एक स्थिर thread_localसदस्य है foo। यदि एक स्थैतिक thread_local Foo fooबनाया जाता है, तो इसका मतलब है कि एक धागा बनाया जाता है।

लेकिन जब मैं कोड चलाता हूं, तो Foo()प्रिंट में कुछ भी नहीं होता है , और यदि मैं टिप्पणी को Barनिर्माणकर्ता में हटा देता हूं , जो उपयोग करता है foo, तो कोड ठीक काम करता है।

मैंने जीसीसी (7.4.0) और क्लैंग (6.0.0) पर यह कोशिश की और परिणाम समान हैं। मुझे लगता है कि संकलक की खोज की गई है fooजो अप्रयुक्त है और एक उदाहरण उत्पन्न नहीं करता है। इसलिए

  1. क्या कंपाइलर ने static thread_localसदस्य की अनदेखी की ? मैं इसके लिए डीबग कैसे कर सकता हूं?
  2. यदि हां, तो एक सामान्य staticसदस्य को यह समस्या क्यों नहीं है?

जवाबों:


9

आपके अवलोकन में कोई समस्या नहीं है। [basic.stc.static] / 2 स्टैटिक स्टोरेज अवधि वाले वैरिएबल को समाप्त करने पर रोक लगाता है:

यदि स्थिर भंडारण अवधि वाले एक चर में साइड इफेक्ट्स के साथ आरंभीकरण या एक विध्वंसक होता है, तो इसका उपयोग नहीं किया जा सकता है भले ही यह अप्रयुक्त प्रतीत हो, सिवाय इसके कि कोई क्लास ऑब्जेक्ट या उसकी प्रति / चाल को समाप्त किया जा सकता है जैसा कि [class.copy] में निर्दिष्ट किया गया है। ।

यह प्रतिबंध अन्य भंडारण अवधि के लिए मौजूद नहीं है। वास्तव में, [basic.stc.thread] / 2 कहते हैं:

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

इससे पता चलता है कि थ्रेड स्टोरेज अवधि के साथ एक चर का निर्माण तब तक नहीं किया जाना चाहिए जब तक कि ओड-यूज़ न किया जाए।


लेकिन यह विसंगति क्यों है?

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

थ्रेड स्थानीय भंडारण अवधि के लिए, हालांकि, एक समस्या है: एक एल्गोरिथ्म बहुत सारे थ्रेड शुरू कर सकता है। इनमें से अधिकांश थ्रेड्स के लिए, चर पूरी तरह से अप्रासंगिक है। यह प्रफुल्लित करने वाला होगा यदि एक बाहरी भौतिकी सिमुलेशन लाइब्रेरी जो कॉल को std::reduce(std::execution::par_unseq, first, last)बहुत सारे fooउदाहरणों का निर्माण करता है, है ना?

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


1
ठीक है ... अगर मानक ने ऐसा कहा, तो ऐसा हो ... लेकिन क्या यह अजीब नहीं है कि समिति स्थैतिक भंडारण अवधि के रूप में दुष्प्रभावों पर विचार नहीं करती है?
रावेनिसादस्क

@reavenisadesk अद्यतन उत्तर देखें।
LF

1

मुझे यह जानकारी " ELF हैंडलिंग फॉर थ्रेड-लोकल स्टोरेज " में मिली, जो @LF के उत्तर को प्रमाणित कर सकता है

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.