2016-06-22 20 views
7

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

//these are initiated in the constructor 
int* data; 
std::atomic<size_t> size; 

void write(int value) { 
    //wait here while "read_lock" 
    //set "write_lock" to "write_lock" + 1 
    auto slot = size.fetch_add(1, std::memory_order_acquire); 
    data[slot] = value; 
    //set "write_lock" to "write_lock" - 1 
} 

लेखन के आदेश महत्वपूर्ण नहीं है, सब मैं यहाँ की जरूरत है हर हालांकि कभी-कभार

हर एक अद्वितीय स्लॉट में जाने के लिए लिखने के लिए, मैं एक धागा आवश्यकता को पढ़ने के लिए इस फ़ंक्शन का उपयोग करने वाला डेटा

int* read() { 
    //set "read_lock" to true 
    //wait here while "write_lock" 
    int* ret = data; 
    data = new int[capacity]; 
    size = 0; 
    //set "read_lock" to false 
    return ret; 
} 
,210

तो यह मूल रूप बफर बाहर स्वैप और एक पुरानी

सिद्धांत रूप में यह 2 ऑपरेटिंग स्थितियों के लिए नेतृत्व करना चाहिए (मैं के टुकड़े कम करने के लिए हटा दिया क्षमता तर्क है) देता है:

1 - सिर्फ एक कंटेनर में लिखने वाले धागे का गुच्छा

2 - जब कुछ धागे पढ़ने के कार्य को निष्पादित करते हैं, तो सभी नए लेखकों को इंतजार करना होगा, पाठक तब तक इंतजार करेंगे जब तक कि सभी मौजूदा लेखन समाप्त नहीं हो जाते हैं, फिर यह पढ़ा तर्क और परिदृश्य 1 करेगा जारी रख सकते हैं।

प्रश्न बात यह है कि मैं एक बाधा किस तरह ताले के लिए उपयोग करने के लिए पता नहीं है है -

के बाद से वहाँ इस तरह के कई कंटेनर हैं और वे सभी CPU चक्र की जरूरत है एक spinlock बेकार होगा

मुझे नहीं पता कि std :: mutex को कैसे लागू किया जाए क्योंकि मैं केवल फ़ंक्शन फ़ंक्शन ट्रिगर होने पर लिखने का कार्य एक महत्वपूर्ण खंड में होना चाहता हूं। एक म्यूटेक्स में पूरे लेखन समारोह को लपेटने से परिचालन परिदृश्य के लिए अनावश्यक मंदी हो जाएगी।

तो यहां इष्टतम समाधान क्या होगा?

+0

मुझे लगता है कि यह प्रश्न उपयोगी हो सकता है https://stackoverflow.com/questions/14306797/c11-equivalent-to-boost-shared-mutex –

+0

तो आपको लेखक धागे को स्वयं के बीच सिंक्रनाइज़ करने की आवश्यकता नहीं है क्योंकि वे हमेशा डेटा के अद्वितीय भागों तक पहुंचें? – Galik

+0

@ गैलिक इस मामले में लेखक धागे को एआई, यूआई और विभिन्न आवृत्तियों पर चल रहे विश्व सिमुलेशन प्रभावों को पूल किया गया है, मुझे अपने आउटपुट को जीपीयू में पाइप करने की ज़रूरत है जो अपने स्वयं के अपडेट अंतराल पर चल रहा है लेकिन उनके पास एक दूसरे के साथ कुछ लेना देना नहीं है परिवहन के दौरान – user81993

उत्तर

2

यदि आपके पास C++14 क्षमता है तो आप पाठकों और लेखकों को अलग करने के लिए std::shared_timed_mutex का उपयोग कर सकते हैं। इस परिदृश्य में ऐसा लगता है कि आपको अपने लेखक धागे साझा पहुंच (अन्य लेखक धागे को एक ही समय में अनुमति देने) और आपके पाठक धागे अद्वितीय पहुंच (सभी अन्य धागे को लात मारना) देने की आवश्यकता है।

तो कुछ इस तरह हो सकता है कि तुम क्या जरूरत है:

class MyClass 
{ 
public: 
    using mutex_type = std::shared_timed_mutex; 
    using shared_lock = std::shared_lock<mutex_type>; 
    using unique_lock = std::unique_lock<mutex_type>; 

private: 
    mutable mutex_type mtx; 

public: 

    // All updater threads can operate at the same time 
    auto lock_for_updates() const 
    { 
     return shared_lock(mtx); 
    } 

    // Reader threads need to kick all the updater threads out 
    auto lock_for_reading() const 
    { 
     return unique_lock(mtx); 
    } 
}; 

// many threads can call this 
void do_writing_work(std::shared_ptr<MyClass> sptr) 
{ 
    auto lock = sptr->lock_for_updates(); 

    // update the data here 
} 

// access the data from one thread only 
void do_reading_work(std::shared_ptr<MyClass> sptr) 
{ 
    auto lock = sptr->lock_for_reading(); 

    // read the data here 
} 

shared_lock रों अन्य थ्रेड एक ही समय में एक shared_lock हासिल लेकिन एक unique_lock एक साथ पहुंचने से रोकने के लिए अनुमति देते हैं। जब एक पाठक धागा unique_lock प्राप्त करने का प्रयास करता है तो सभी shared_lock एस unique_lock को विशेष नियंत्रण प्राप्त होने से पहले खाली कर दिया जाएगा।

+0

हां। आरडब्ल्यू लॉक जाने का एक तरीका लगता है। हालांकि, यह * निष्पक्षता * प्रश्नों का एक गुच्छा उठाता है। – SergeyA

-1

आप इसे साझा करने के बजाय नियमित म्यूटेक्स और हालत चर के साथ भी कर सकते हैं। माना जाता है कि shared_mutex में उच्च ओवरहेड है, इसलिए मुझे यकीन नहीं है कि कौन तेज़ होगा। गैलिक के समाधान के साथ आप संभावित रूप से साझा 0 m35xकॉल पर साझा म्यूटेक्स को लॉक करने के लिए भुगतान कर रहे हैं; मुझे आपकी पोस्ट से इंप्रेशन मिला है कि write को पढ़ने से अधिक रास्ता कहा जाता है, इसलिए शायद यह अवांछित है।

int* data; // initialized somewhere 
std::atomic<size_t> size = 0; 
std::atomic<bool> reading = false; 
std::atomic<int> num_writers = 0; 
std::mutex entering; 
std::mutex leaving; 
std::condition_variable cv; 

void write(int x) { 
    ++num_writers; 
    if (reading) { 
     --num_writers; 
     if (num_writers == 0) 
     { 
      std::lock_guard l(leaving); 
      cv.notify_one(); 
     }   
     { std::lock_guard l(entering); } 
     ++num_writers; 
    } 
    auto slot = size.fetch_add(1, std::memory_order_acquire); 
    data[slot] = x; 
    --num_writers; 
    if (reading && num_writers == 0) 
    { 
     std::lock_guard l(leaving); 
     cv.notify_one(); 
    } 
} 

int* read() { 
    int* other_data = new int[capacity]; 
    { 
     std::unique_lock enter_lock(entering); 
     reading = true; 
     std::unique_lock leave_lock(leaving); 
     cv.wait(leave_lock, []() { return num_writers == 0; }); 
     swap(data, other_data); 
     size = 0; 
     reading = false; 
    } 
    return other_data; 
} 

यह थोड़ा जटिल है और मुझे काम करने के लिए कुछ समय लगा, लेकिन मुझे लगता है कि यह उद्देश्य को अच्छी तरह से पूरा करना चाहिए।

सामान्य मामले में जहां केवल लेखन हो रहा है, पढ़ना हमेशा झूठा होता है। तो आप सामान्य करते हैं, और दो अतिरिक्त परमाणु वृद्धि और दो untaken शाखाओं के लिए भुगतान करते हैं। इसलिए किसी साझा म्यूटेक्स से जुड़े समाधान के विपरीत, सामान्य पथ को किसी भी म्यूटेक्स को लॉक करने की आवश्यकता नहीं होती है, यह माना जाता है कि यह महंगा है: http://permalink.gmane.org/gmane.comp.lib.boost.devel/211180

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

इस बीच, पढ़ा धागा अब इस शर्त पर इंतजार कर रहा है कि लेखकों की संख्या 0 है। यदि हम भाग्यशाली हैं, तो यह वास्तव में तुरंत हो सकता है। हालांकि, num_writers को बढ़ाने और घटाने के बीच दोनों स्थानों में से किसी एक में लिखने के धागे हैं, तो यह नहीं होगा। प्रत्येक बार जब एक लेखन धागा num_writers घटता है, तो यह जांचता है कि क्या यह संख्या शून्य से कम हो गई है, और जब यह करता है तो यह स्थिति चर को संकेत देगा। चूंकि num_writers परमाणु है जो विभिन्न पुनर्निर्मित शेंगेनियों को रोकता है, यह गारंटी है कि अंतिम धागा num_writers == 0 देखेंगे; इसे एक से अधिक बार अधिसूचित किया जा सकता है लेकिन यह ठीक है और इसका परिणाम खराब व्यवहार नहीं हो सकता है।

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

जैसा कि पहले बताया गया है, सामान्य ऑपरेशन में कोई ताले नहीं हैं, केवल वृद्धि और शाखाएं नहीं हैं। यहां तक ​​कि जब कोई पठन होता है, तब भी पढ़ने वाले धागे में एक ताला और एक शर्त परिवर्तनीय प्रतीक्षा होगी, जबकि एक सामान्य लेखन धागे में एक म्यूटेक्स के लगभग एक ताला/अनलॉक होगा और यह सब (एक, या लिखित धागे की एक छोटी संख्या होगी, एक शर्त परिवर्तनीय अधिसूचना भी करते हैं)।

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