2013-08-16 7 views
8

मैं सी ++ 11 में थ्रेड का परीक्षण करने के लिए एक सरल प्रोग्राम लिखता हूं लेकिन std::cout मेरी अपेक्षा के अनुसार काम नहीं करता है।एकाधिक धागे में std :: cout का उपयोग करके

class Printer 
{ 
public: 
    void exec() 
    { 
     mutex m; 
     m.lock(); 
     cout<<"Hello "<<this_thread::get_id()<<endl; 
     chrono::milliseconds duration(100); 
     this_thread::sleep_for(duration); 
     m.unlock(); 

    } 
}; 

int main() 
{ 
    Printer printer; 

    thread firstThread([&printer](){ 
     while(1) 
      printer.exec(); 

    }); 
    thread secondThread([&printer](){ 
     while(1) 
      printer.exec(); 
    }); 

    firstThread.join(); 
    secondThread.join();  
} 
परिणाम के कुछ

:

Hello 11376 
Hello 16076 
Hello 16076 
Hello Hello 11376 
16076 
Hello 11376 
,.... 

मैं धागे ताला लगा तो मैं नहीं कर सकते क्यों दो धागे एक ही समय में std::cout को क्रियान्वित कर रहे हैं के लिए म्युटेक्स इस्तेमाल किया। यह मेरे लिए बहुत कमजोर है। क्या कोई भी समझा सकता है कि क्या हो रहा है!?

+1

एक बार जब आप उत्तर में दिए गए समाधान को लागू कर लेते हैं तो आपको मैन्युअल लॉक/अनलॉक कॉल के बजाय 'lock_guard' का उपयोग करने के लिए भी जाना चाहिए। और फ्लशिंग परेशान न करें (उदा।, 'Endl' के साथ); बस '\ n' का प्रयोग करें। – bames53

उत्तर

21

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

पोस्ट कोड में सही करने के लिए, mutex एक सदस्य चर बनाओ। हालांकि, अगर Printer ऑब्जेक्ट बनाया गया था तो वहां Printer उदाहरणों का उपयोग करने वाले थ्रेड के बीच कोई सिंक्रनाइज़ेशन नहीं होगा।

class Printer 
{ 
public: 
    //... 
private: 
    static std::mutex mtx_; 
}; 

std::mutex Printer::mtx_; 

सुनिश्चित करने के लिए एक mutex हमेशा जारी की है, एक सामान्य रूप से कार्य करें या अपवाद के माध्यम से, std:lock_guard का उपयोग बाहर निकल जाता है भले ही कोई: इस मामले में, mutex तुल्यकालन सुनिश्चित करने के लिए एक static सदस्य चर की आवश्यकता होगी:

std::lock_guard<std::mutex> lock(m); // 'm' locked, and will be 
            // unlocked when 'lock' is destroyed. 
std::cout<< "Hello " << std::this_thread::get_id() << std::endl; 
std::chrono::milliseconds duration(100); 
std::this_thread::sleep_for(duration); 
+0

'स्थिर' भंडारण अवधि के साथ 'mutex' घोषित करना ठीक है। ... या वो। – Casey

+0

@Casey, या धागे के रूप में एक सदस्य चर एक ही 'प्रिंटर' उदाहरण का उपयोग कर रहे हैं। – hmjd

+1

@hjmd मैं इस मामले में 'std :: cout' के दायरे के अनुरूप' स्थिर 'पसंद करूंगा। इस तरह यह काम जारी रखेगा जब कुछ रखरखाव प्रोग्रामर लाइन के नीचे किसी अन्य कार्य में 'प्रिंटर' बनाता है। इस मामले में – Casey

1

आप एक वैश्विक std::mutex cout_mutex; (कहीं आपके नामस्थान में) है, जो संरक्षित std::cout उत्पादन के लिए प्रयोग किया जाता है पर विचार कर सकते। सुनिश्चित करें कि आप std::lock<std::mutex> का उपयोग करें (इसलिए आप mutex को अनलॉक करने और अपवाद सुरक्षा के लिए भूल नहीं सकते हैं)।

11

स्वीकृत उत्तर सही है। हालांकि चिंताएं अलग करना अच्छा है:

  1. आपको std::cout पर थ्रेड सुरक्षित तरीके से प्रिंट करने का एक तरीका चाहिए।
  2. आपको थ्रेड में चलाने और उन्हें लॉन्च करने के लिए ऑब्जेक्ट/फ़ैक्टर/फ़ंक्शन बनाने की आवश्यकता है।

    #include <iostream> 
    #include <mutex> 
    
    std::ostream& 
    print_one(std::ostream& os) 
    { 
        return os; 
    } 
    
    template <class A0, class ...Args> 
    std::ostream& 
    print_one(std::ostream& os, const A0& a0, const Args& ...args) 
    { 
        os << a0; 
        return print_one(os, args...); 
    } 
    
    template <class ...Args> 
    std::ostream& 
    print(std::ostream& os, const Args& ...args) 
    { 
        return print_one(os, args...); 
    } 
    
    std::mutex& 
    get_cout_mutex() 
    { 
        static std::mutex m; 
        return m; 
    } 
    
    template <class ...Args> 
    std::ostream& 
    print(const Args& ...args) 
    { 
        std::lock_guard<std::mutex> _(get_cout_mutex()); 
        return print(std::cout, args...); 
    } 
    

    इस कोड को ऊपर std::cout के अलावा अन्य धाराओं के लिए पुन: उपयोग किया जा सकता है लेकिन है:

यहाँ एक उपयोगिता मैं का उपयोग करें कि सिर्फ std::cout तर्क संग्रह और के तहत एक static std::mutex उन्हें बाहर स्ट्रीमिंग पर ध्यान केंद्रित करता है केवल std::cout को लक्षित करने के लिए विशिष्ट। इसके साथ ही अपने Printer::exec() अब काफी सरल किया जा सकता:

void exec() 
{ 
    print("Hello ", std::this_thread::get_id(), '\n'); 
    std::this_thread::sleep_for(std::chrono::milliseconds(100)); 
} 

अब, केवल अपने Printer उपयोग cout एक threadsafe ढंग से, और सरलीकृत किया गया है नहीं होगा (जैसे cout के लिए अपने mutex को बनाए रखने की आवश्यकता नहीं है), लेकिन आपके अन्य सभी प्रकार और फ़ंक्शंस cout का भी उपयोग कर सकते हैं और सभी सुरक्षित रूप से एक साथ इंटरऑपरेट कर सकते हैं। print फ़ंक्शन स्वयं ही mutex बनाए रखता है, और यह तथ्य print के सभी ग्राहकों से दूर हो गया है।

+5

प्रिंट एक टेम्पलेट फ़ंक्शन है। मुझे लगता है कि आप एक से अधिक mutex के साथ खत्म हो सकता है। –

+1

@JanChristophUhde: उत्कृष्ट अवलोकन! और किसी को नोटिस करने में केवल 3 साल लग गए! :-) फिक्स्ड। धन्यवाद। –

3

मैं this question में दिए गए निकोलस से चाल साझा कर रहा हूं जो मुझे हॉवर्ड हिन्नेंट कार्यान्वयन से अधिक सुरुचिपूर्ण लगता है। विचार अस्थायी ostringstream ऑब्जेक्ट बनाने और विनाशक पर सुरक्षा कोड डालना है।

/** Thread safe cout class 
    * Exemple of use: 
    * PrintThread{} << "Hello world!" << std::endl; 
    */ 
class PrintThread: public std::ostringstream 
{ 
public: 
    PrintThread() = default; 

    ~PrintThread() 
    { 
     std::lock_guard<std::mutex> guard(_mutexPrint); 
     std::cout << this->str(); 
    } 

private: 
    static std::mutex _mutexPrint; 
}; 

std::mutex PrintThread::_mutexPrint{}; 

फिर आप एक नियमित रूप से std::cout के रूप में उपयोग कर सकते हैं, किसी भी धागे से:

PrintThread{} << "val = " << 33 << std::endl; 

वस्तु एक नियमित std::ostringstream के रूप में डेटा एकत्र। जैसे ही कोमा पहुंचा जाता है, वस्तु नष्ट हो जाती है और सभी एकत्रित जानकारी को फ्लश कर दिया जाता है।

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