c ++ लूप प्रिंट के लिए थ्रेड्स गलत मान प्रिंट करता है


19

मैं सी + + में मल्टी-थ्रेडिंग को समझने की कोशिश कर रहा हूं, लेकिन मैं इस समस्या में फंस गया हूं: अगर मैं थ्रेड को लूप में लॉन्च करता हूं तो वे गलत मान लेते हैं। यह कोड है:

#include <iostream>
#include <list>
#include <thread>

void print_id(int id){
    printf("Hello from thread %d\n", id);
}

int main() {
    int n=5;
    std::list<std::thread> threads={};
    for(int i=0; i<n; i++ ){
        threads.emplace_back(std::thread([&](){ print_id(i); }));
    }
    for(auto& t: threads){
        t.join();
    }
    return 0;
}

मैं 0,1,2,3,4 मूल्यों को मुद्रित करने की उम्मीद कर रहा था, लेकिन मुझे अक्सर दो बार समान मूल्य मिला। यह आउटपुट है:

Hello from thread 2
Hello from thread 3
Hello from thread 3
Hello from thread 4
Hello from thread 5

मुझे क्या याद आ रहा है?


7
दर्रा i, लैम्ब्डा के लिए मूल्य द्वारा [i]
rafix07 10

1
यह ध्यान देने योग्य है कि आपका उपयोग emplace_backविषम है: emplace_backतर्कों की एक सूची लेता है और इसके लिए एक निर्माणकर्ता को पास करता है std::thread। आपने (रवेल) उदाहरण पास किया है std::thread, इसलिए एक थ्रेड का निर्माण करेंगे, फिर उस थ्रेड को वेक्टर में ले जाएँ। यह ऑपरेशन अधिक सामान्य विधि द्वारा बेहतर ढंग से व्यक्त किया गया है push_back। यह या तो लिखने के लिए अधिक समझदार होगा threads.emplace_back([i](){ print_id(i); });(जगह में निर्माण) या threads.push_back(std::thread([i](){ print_id(i); }));(निर्माण + चाल) जो कुछ हद तक अधिक मुहावरेदार हैं।
मिलो ब्रांड

जवाबों:


17

[&]वाक्य रचना खड़ी कर रहा है iपर कब्जा कर लिया जा करने के लिए संदर्भ द्वारा । इसलिए अक्सर iतब और अधिक उन्नत होगा जब धागा आपकी अपेक्षा से अधिक चलता है। अधिक गंभीरता से, आपके कोड का व्यवहार अपरिभाषित है यदि iकोई थ्रेड चलने से पहले दायरे से बाहर हो जाता है।

iमूल्य से पकड़ना - यानी std::thread([i](){ print_id(i); })फिक्स है।


2
या कम उपयोग किया जाता है और अक्सर उचित नहीं होता हैstd::thread([=](){ print_id(i); })
वांडर्र 3

3
व्यवहार पहले से ही अपरिभाषित है क्योंकि यह iमुख्य थ्रेड लेखन और अन्य सूत्र पढ़ने के साथ (गैर-परमाणु) पर एक डेटा दौड़ है ।
अखरोट

6

दो समस्याएं:

  1. थ्रेड चलने पर आपका कोई नियंत्रण नहीं है, जिसका अर्थ iहै कि लैम्ब्डा में वेरिएबल का मूल्य वह नहीं हो सकता है जिसकी आप अपेक्षा करते हैं।

  2. चर iकेवल लूप और लूप के लिए स्थानीय है। यदि लूप एक या अधिक थ्रेड चलने से पहले खत्म हो जाता है, तो उन थ्रेड्स में एक चर का अमान्य संदर्भ होगा जिसका जीवनकाल समाप्त हो गया है।

आप इन दोनों समस्याओं को केवल संदर्भ के बजाय i मूल्य द्वारा चर पर कब्जा करके हल कर सकते हैं । इसका अर्थ है कि प्रत्येक थ्रेड में मान की एक प्रति होगी , और प्रत्येक थ्रेड के लिए यह प्रतिलिपि विशिष्ट रूप से बनाई जाएगी।


5

एक और बात:
हमेशा तब तक प्रतीक्षा न करें जब तक कि हमेशा एक क्रमबद्ध क्रम न हो: 0, 1, 2, 3, ... क्योंकि मल्टीथ्रेडिंग निष्पादन मोड में एक विशिष्टता है: अनिश्चिततावाद

Indeterminism का मतलब है कि एक ही कार्यक्रम का निष्पादन, एक ही परिस्थितियों में, एक अलग परिणाम देता है।

यह इस तथ्य के कारण है कि ओएस अनुसूचियां कई मापदंडों के आधार पर एक निष्पादन से दूसरे तक अलग-अलग थ्रेड करती हैं: सीपीयू लोड, अन्य प्रक्रियाओं की प्राथमिकता, संभव सिस्टम रुकावट, ...

आपके उदाहरण में केवल 5 धागे हैं, इसलिए यह सरल है, थ्रेड्स की संख्या बढ़ाने की कोशिश करें, और उदाहरण के लिए प्रोसेसिंग फ़ंक्शन में एक नींद डालें, आप देखेंगे कि परिणाम एक निष्पादन से दूसरे तक भिन्न हो सकता है।

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