Share_mutex को बढ़ावा देने के लिए उदाहरण (एकाधिक रीड / वन राइट)?


116

मेरे पास एक मल्टीथ्रेड ऐप है जिसे अक्सर कुछ डेटा को पढ़ना पड़ता है, और कभी-कभी उस डेटा को अपडेट किया जाता है। अभी एक म्यूटेक्स उस डेटा तक पहुंच को सुरक्षित रखता है, लेकिन यह महंगा है क्योंकि मैं चाहूंगा कि एक साथ कई थ्रेड्स एक साथ पढ़ने में सक्षम हों, और केवल एक अपडेट की आवश्यकता होने पर उन्हें लॉक कर दें (अपडेट थ्रेड को समाप्त करने के लिए अन्य थ्रेड्स की प्रतीक्षा कर सकते हैं) ।

मुझे लगता है कि यह वही है जो boost::shared_mutexकरने वाला है, लेकिन मैं इसका उपयोग कैसे करना है, इस बारे में स्पष्ट नहीं हूं, और एक स्पष्ट उदाहरण नहीं मिला है।

क्या किसी के पास एक सरल उदाहरण है जिसे मैं आरंभ करने के लिए उपयोग कर सकता हूं?


1800 सूचना का उदाहरण सही है। यह भी देखें इस अनुच्छेद: क्या बूस्ट धागे में नया क्या है
असफ लवी

जवाबों:


102

ऐसा लगता है कि आप ऐसा कुछ करेंगे:

boost::shared_mutex _access;
void reader()
{
  // get shared access
  boost::shared_lock<boost::shared_mutex> lock(_access);

  // now we have shared access
}

void writer()
{
  // get upgradable access
  boost::upgrade_lock<boost::shared_mutex> lock(_access);

  // get exclusive access
  boost::upgrade_to_unique_lock<boost::shared_mutex> uniqueLock(lock);
  // now we have exclusive access
}

7
यह मेरा पहली बार बूस्ट का उपयोग कर रहा है, और मैं एक सी ++ नौसिखिया हूं, इसलिए हो सकता है कि मैं कुछ याद कर रहा हूं - लेकिन अपने स्वयं के कोड में, मुझे इस प्रकार को निर्दिष्ट करना था, जैसे: बूस्ट: शेयर किया गया :: share_lock <share_mutex> लॉक (_पहुंच);
केन स्मिथ

2
मैं खुद इसका उपयोग करने की कोशिश कर रहा हूं, लेकिन मुझे एक त्रुटि मिल रही है। 'लॉक' से पहले गायब टेम्प्लेट तर्क। कोई विचार?
मैट

2
@ शहज़ादे को डरा दिया जाता है, लेकिन अगर आप ज़रूरत है तो आप उन्हें (.unlock) () के साथ जल्दी रिलीज़ कर सकते हैं।
mmocny

4
मैंने अनुपलब्ध टेम्पलेट तर्क जोड़े हैं।

1
@राज आप अपग्रेड_लॉक प्राप्त कर सकते हैं, लेकिन एक अनूठे लॉक में अपग्रेड करना तब तक अवरुद्ध रहेगा जब तक कि साझा किया गया विमोचन -11 बजे जारी नहीं हो जाता
1800 सूचना

166

1800 सूचना कम या ज्यादा सही है, लेकिन कुछ मुद्दे हैं जिन्हें मैं सही करना चाहता था।

boost::shared_mutex _access;
void reader()
{
  boost::shared_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access
}

void conditional_writer()
{
  boost::upgrade_lock< boost::shared_mutex > lock(_access);
  // do work here, without anyone having exclusive access

  if (something) {
    boost::upgrade_to_unique_lock< boost::shared_mutex > uniqueLock(lock);
    // do work here, but now you have exclusive access
  }

  // do more work here, without anyone having exclusive access
}

void unconditional_writer()
{
  boost::unique_lock< boost::shared_mutex > lock(_access);
  // do work here, with exclusive access
}

यह भी ध्यान दें, एक साझा_लॉक के विपरीत, केवल एक धागा एक बार में एक उन्नयन_लॉक प्राप्त कर सकता है, तब भी जब वह अपग्रेड न हो (जो मुझे लगा कि जब मैं उसमें भाग गया था तो अजीब था)। इसलिए, यदि आपके सभी पाठक सशर्त लेखक हैं, तो आपको एक और समाधान खोजने की आवश्यकता है।


1
बस "एक और समाधान" पर टिप्पणी करने के लिए। जब मेरे सभी पाठक जहां सशर्त लेखक हैं, तो मैंने जो किया था, वह हमेशा एक साझा_लोक प्राप्त करता था, और जब मुझे निजीकरण लिखने के लिए अपग्रेड करने की आवश्यकता होती है, तो मैं (.unlock) पाठक ताला लगाता हूं और एक नया यूनीक_लोक प्राप्त करता हूं। यह आपके आवेदन के तर्क को जटिल करेगा, और जब आप पहली बार पढ़ते हैं तो राज्य को बदलने के लिए अन्य लेखकों के लिए अवसर की एक खिड़की है।
mmocny

8
क्या रेखा boost::unique_lock< boost::shared_mutex > lock(lock);को boost::unique_lock< boost::shared_mutex > lock( _access नहीं पढ़ना चाहिए); ?
स्टीवविल्किंसन

4
वह आखिरी कैवेट बहुत अजीब है। यदि एक बार में केवल एक थ्रेड अपग्रेड_लॉक पकड़ सकता है, तो अपग्रेड_लॉक और यूनिक_लॉक में क्या अंतर है?
केन स्मिथ

2
@ मैं बहुत स्पष्ट नहीं था, लेकिन अपग्रेड_लॉक का लाभ यह है कि अगर वर्तमान में कुछ शेयर्ड_लॉक प्राप्त नहीं हुए हैं (तो कम से कम जब तक आप यूनिक में अपग्रेड नहीं करते हैं) ब्लॉक नहीं हो जाता। हालाँकि, अपग्रेड_लॉक को आज़माने और प्राप्त करने के लिए दूसरा थ्रेड ब्लॉक करेगा, भले ही पहले ने यूनिक को अपग्रेड न किया हो, जिसकी मुझे उम्मीद नहीं थी।
16

6
यह एक चर्चित बढ़ावा मुद्दा है। यह 1.50 बीटा को बढ़ावा देने के लिए हल किया गया लगता है: svn.boost.org/trac/boost/ticket/5516
Shilon

47

C ++ 17 (VS2015) के बाद से आप रीड-राइट लॉक के लिए मानक का उपयोग कर सकते हैं:

#include <shared_mutex>

typedef std::shared_mutex Lock;
typedef std::unique_lock< Lock > WriteLock;
typedef std::shared_lock< Lock > ReadLock;

Lock myLock;


void ReadFunction()
{
    ReadLock r_lock(myLock);
    //Do reader stuff
}

void WriteFunction()
{
     WriteLock w_lock(myLock);
     //Do writer stuff
}

पुराने संस्करण के लिए, आप एक ही सिंटैक्स के साथ बूस्ट का उपयोग कर सकते हैं:

#include <boost/thread/locks.hpp>
#include <boost/thread/shared_mutex.hpp>

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock >  WriteLock;
typedef boost::shared_lock< Lock >  ReadLock;

5
मैं भी कहूंगा typedef boost::unique_lock< Lock > WriteLock; typedef boost::shared_lock< Lock > ReadLock;
दाखलता

6
पूरे thread.hpp को शामिल करने की आवश्यकता नहीं है। यदि आपको केवल ताले की आवश्यकता है, तो ताले शामिल करें। यह आंतरिक कार्यान्वयन नहीं है। न्यूनतम को शामिल करें।
योचाई टिमर

5
निश्चित रूप से सबसे सरल कार्यान्वयन लेकिन मुझे लगता है कि यह म्यूटेक्स और लॉक दोनों को लॉक्स के रूप में संदर्भित करने के लिए भ्रमित है। एक म्यूटेक्स एक म्यूटेक्स है, एक लॉक एक ऐसी चीज है जो इसे बंद अवस्था में रखता है।
टिम एमबी

17

बस कुछ और अनुभवजन्य जानकारी जोड़ने के लिए, मैं अपग्रेडेबल तालों के पूरे मामले की जांच कर रहा हूं, और Share_mutex को बढ़ावा देने के लिए उदाहरण (कई पढ़ता / लिखता हूं)? एक महत्वपूर्ण उत्तर है जो महत्वपूर्ण जानकारी को जोड़ता है कि केवल एक थ्रेड में एक उन्नयन_लॉक हो सकता है भले ही वह अपग्रेड न हो, यह महत्वपूर्ण है क्योंकि इसका मतलब है कि आप साझा लॉक को पहले साझा किए बिना एक अद्वितीय लॉक से एक अद्वितीय लॉक में अपग्रेड नहीं कर सकते। (यह कहीं और चर्चा की गई है लेकिन सबसे दिलचस्प धागा यहाँ है। http://thread.gmane.org/gmane.comp.lib.boost.devel/214394 )

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

  1. जो धागा शेयर्ड_मुटेक्स पर अनूठे_लोक के लिए इंतजार कर रहा है, वह आने वाले किसी भी नए पाठक को रोकता है, उन्हें लेखकों के अनुरोध की प्रतीक्षा करनी होगी। यह सुनिश्चित करता है कि पाठक लेखकों को भूखा न रखें (हालांकि मेरा मानना ​​है कि लेखक पाठकों को भूखा रख सकते हैं)।

  2. वह थ्रेड जो अपग्रेड करने योग्य_लॉक के अपग्रेड की प्रतीक्षा कर रहा है, अन्य थ्रेड्स को साझा लॉक प्राप्त करने की अनुमति देता है, इसलिए यदि पाठकों को बहुत बार देखा जाता है तो यह थ्रेड भूखा रह सकता है।

यह विचार करने के लिए एक महत्वपूर्ण मुद्दा है, और शायद दस्तावेज होना चाहिए।


3
यह Terekhov algorithmसुनिश्चित करता है कि 1., लेखक पाठकों को भूखा न रख सके। देखें इस । लेकिन 2.सच है। एक उन्नयन_लॉक निष्पक्षता की गारंटी नहीं देता है। देखें इस
जोनासवथेरिन

2

एक गिनती के साथ एक सेमाफोर का उपयोग करें जो पाठकों की संख्या के बराबर है। प्रत्येक पाठक को पढ़ने के लिए सेमाफोर की एक-एक गिनती लेने दें, इस तरह वे सभी एक ही समय में पढ़ सकते हैं। फिर लेखक को लिखने से पहले सभी सेमाफोर की गिनती करने दें। इसके कारण लेखक को सभी लेखों को समाप्त करने के लिए इंतजार करना पड़ता है और फिर लिखते समय रीड्स को ब्लॉक करना पड़ता है।


(1) आप एक लेखक एक मनमाना राशि से गिनती घटती कैसे कर सकता हूँ atomically ? (२) यदि लेखक किसी तरह से गिनती को शून्य तक बढ़ाता है, तो लिखने से पहले ही चल रहे पाठकों के खत्म होने का इंतजार कैसे करता है?
तोक शीलोन

बुरा विचार: यदि दो लेखक एक साथ पहुंचने का प्रयास करते हैं, तो आपके पास एक गतिरोध हो सकता है।
Caduchon

2

जिम मॉरिस द्वारा शानदार प्रतिक्रिया, मैं इस पर अड़ गया और मुझे यह पता लगाने में थोड़ा समय लगा। यहाँ कुछ सरल कोड है जो दिखाता है कि एक यूनिक_लॉक बूस्ट (संस्करण 1.54) के लिए "रिक्वेस्ट" सबमिट करने के बाद सभी शेयर्ड_लॉक अनुरोध ब्लॉक हो जाते हैं। यह बहुत दिलचस्प है क्योंकि मुझे ऐसा लगता है कि अनूठे_लॉक और अपग्रेडेबल_लॉक के बीच चयन करने की अनुमति मिलती है अगर हम प्राथमिकता या कोई प्राथमिकता नहीं लिखना चाहते हैं।

जिम मॉरिस के पोस्ट में भी (1) इसके विपरीत प्रतीत होता है: Boost साझा_लॉक। पसंद किया गया पढ़ें?

#include <iostream>
#include <boost/thread.hpp>

using namespace std;

typedef boost::shared_mutex Lock;
typedef boost::unique_lock< Lock > UniqueLock;
typedef boost::shared_lock< Lock > SharedLock;

Lock tempLock;

void main2() {
    cout << "10" << endl;
    UniqueLock lock2(tempLock); // (2) queue for a unique lock
    cout << "11" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(1));
    lock2.unlock();
}

void main() {
    cout << "1" << endl;
    SharedLock lock1(tempLock); // (1) aquire a shared lock
    cout << "2" << endl;
    boost::thread tempThread(main2);
    cout << "3" << endl;
    boost::this_thread::sleep(boost::posix_time::seconds(3));
    cout << "4" << endl;
    SharedLock lock3(tempLock); // (3) try getting antoher shared lock, deadlock here
    cout << "5" << endl;
    lock1.unlock();
    lock3.unlock();
}

मुझे वास्तव में यह समझने में परेशानी हो रही है कि उपरोक्त कोड डेडलॉक क्यों है जबकि कोड [ stackoverflow.com/questions/12082405/…] में काम करता है।
dale1209

1
यह वास्तव में (2) में गतिरोध करता है, (3) में नहीं, क्योंकि (2) अपने लॉक को जारी करने के लिए (1) की प्रतीक्षा कर रहा है। याद रखें: एक अद्वितीय लॉक प्राप्त करने के लिए, आपको सभी मौजूदा साझा किए गए लॉक को समाप्त करने के लिए इंतजार करना होगा।
जोनासवैथरीन

@JonesV, भले ही (2) सभी साझा किए गए तालों को खत्म करने के लिए इंतजार करता है, यह गतिरोध नहीं होगा क्योंकि यह एक अलग धागा है जिसे अधिग्रहीत किया गया है (1), अगर लाइन (3) मौजूद नहीं थी, तो कार्यक्रम कोई गतिरोध नहीं है।
सागीलोव
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.