2016-12-16 6 views
7

boost::shared_mutex या std::shared_mutex (सी ++ 17) एकल लेखक, एकाधिक पाठक पहुंच के लिए उपयोग किया जा सकता है। एक शैक्षणिक अभ्यास के रूप में, मैंने एक सरल कार्यान्वयन किया जो स्पिनलॉकिंग का उपयोग करता है और इसमें अन्य सीमाएं हैं (उदाहरण के लिए निष्पक्षता नीति), लेकिन स्पष्ट रूप से वास्तविक अनुप्रयोगों में उपयोग करने का इरादा नहीं है।सी ++ shared_mutex कार्यान्वयन

विचार यह है कि म्यूटेक्स एक संदर्भ गणना रखता है जो शून्य नहीं है अगर कोई थ्रेड लॉक नहीं रखता है। यदि> 0, मान उन पाठकों की संख्या का प्रतिनिधित्व करता है जिनके पास पहुंच है। यदि -1, एक लेखक के पास पहुंच है।

क्या यह सही कार्यान्वयन है (विशेष रूप से उपयोग किए गए, न्यूनतम, मेमोरी ऑर्डरिंग के साथ) जो डेटा दौड़ से मुक्त है?

#include <atomic> 

class my_shared_mutex { 
    std::atomic<int> refcount{0}; 
public: 

    void lock() // write lock 
    { 
     int val; 
     do { 
      val = 0; // Can only take a write lock when refcount == 0 

     } while (!refcount.compare_exchange_weak(val, -1, std::memory_order_acquire)); 
     // can memory_order_relaxed be used if only a single thread takes write locks ? 
    } 

    void unlock() // write unlock 
    { 
     refcount.store(0, std::memory_order_release); 
    } 

    void lock_shared() // read lock 
    { 
     int val; 
     do { 
      do { 
       val = refcount.load(std::memory_order_relaxed); 

      } while (val == -1); // spinning until the write lock is released 

     } while (!refcount.compare_exchange_weak(val, val+1, std::memory_order_acquire)); 
    } 

    void unlock_shared() // read unlock 
    { 
     refcount.fetch_sub(1, std::memory_order_relaxed); 
    } 
}; 

उत्तर

3

(मैं सी ++ compare_exchange_weak समारोह, नहीं cmpxchg instruction के लिए आशुलिपि के रूप में cmpxchg उपयोग कर रहा हूँ)।

lock_shared निश्चित रूप से अच्छा दिखता है: पढ़ने पर कताई और केवल cmpxchg का प्रयास करने पर जब cmpxchg पर कताई से प्रदर्शन के लिए मूल्य बेहतर होता है। हालांकि मुझे लगता है कि आपको उसमें मजबूती के लिए मजबूर होना पड़ा, -1 से 0 को बदलने और लिखने-लॉक को अनलॉक करने से बचने के लिए।

मुझे लगता है कि unlock_sharedmo_release, नहीं mo_relaxed का उपयोग करना चाहिए, क्योंकि यह यकीन है कि एक लेखक से पहले पाठक महत्वपूर्ण अनुभाग से लोड हो लेखन शुरू नहीं करता है बनाने के लिए साझा डेटा संरचना से लोड ऑर्डर करने के लिए की जरूरत है। (LoadStore reordering कमजोर आदेशित आर्किटेक्चर पर एक बात है, भले ही x86 केवल स्टोरलोड रीडरिंग करता है।) A Release operation will order preceding loads and keep them inside the critical section


(लिखने lock में): // यदि केवल एक ही धागे ताले लिख लेता है इस्तेमाल किया जा memory_order_relaxed कर सकते हैं?

नहीं है, तो आप अभी भी महत्वपूर्ण अनुभाग के अंदर लेखन रखने के लिए, की जरूरत है ताकि cmpxchg अभी भी unlock_shared से साथ सिंक्रनाइज़ करने की जरूरत है (सी ++ शब्दावली में) जारी-भंडार।

+0

मुझे अनलॉक_श्रेड में मेमोरी ऑर्डर के बारे में निश्चित नहीं था, लेकिन मेरा तर्क यह था कि यह वास्तव में कुछ भी 'रिलीज़' नहीं करता है क्योंकि इसमें केवल पढ़ने के लिए पहुंच है और – LWimsey

+0

@LWimsey की सुरक्षा वाले डेटा को नहीं बदला जा सकता है: हाँ, यह कठिन है स्टोर ऑर्डरिंग से लोड ऑर्डरिंग के बारे में सोचने के लिए, लेकिन यह एक असली बात है। इस समय एक लोड वैश्विक रूप से दिखाई देता है जब यह एल 1 कैश से डेटा पढ़ता है। (क्योंकि वह तब होता है जब यह एक ही सीपीयू के आउट-ऑफ-ऑर्डर कोर में वैश्विक रूप से सुसंगत कैश से एक प्रतिलिपि बनाता है।) –

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