2013-05-15 14 views
5

में एक विनाशक में एक म्यूटेक्स लॉक करना मेरे पास कुछ कोड है जो थ्रेड सुरक्षित और अपवाद सुरक्षित होना चाहिए।सी ++ 11

#include <mutex> 
#include <thread> 

std::mutex mutex; 
int n=0; 

class Counter{ 
public: 
    Counter(){ 
     std::lock_guard<std::mutex>guard(mutex); 
     n++;} 
    ~Counter(){ 
     std::lock_guard<std::mutex>guard(mutex);//How can I protect here the underlying code to mutex.lock() ? 
     n--;} 
}; 

void doSomething(){ 
    Counter counter; 
    //Here I could do something meaningful 
} 

int numberOfThreadInDoSomething(){ 
    std::lock_guard<std::mutex>guard(mutex); 
    return n;} 

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

मैं क्या कर सकता हूं?

0) मैं एक परमाणु चर (बेशक यह चाल यहाँ करना होगा साथ n को बदल नहीं सकते, लेकिन वह मेरे सवाल के बिंदु)

1) मैं एक स्पिन के साथ मेरी म्युटेक्स ताला

बदल सकते नहीं है

2) मैं लॉकिंग को अनंत लूप में तब तक कोशिश कर सकता हूं जब तक कि मैं लॉक प्राप्त नहीं करता, बिना किसी अपवाद के

इनमें से कोई भी समाधान बहुत आकर्षक प्रतीत नहीं होता है। क्या आपको एक ही समस्या है? आपने इसे कैसे ठीक किया ?

+3

'मेरे पास एक म्यूटेक्स है जिसे मुझे ऑब्जेक्ट के विनाशक में लॉक करने की आवश्यकता है' - एक बुरा विचार की तरह लगता है। हमें समाधान प्रदान करने और इसके साथ समस्याओं को ठीक करने के बजाय, आपको हमें यह बताने चाहिए कि आप किस समस्या को हल करने की कोशिश कर रहे हैं, इसलिए हम एक बेहतर समाधान प्रदान कर सकते हैं। –

+1

@RobertHarvey मैं वास्तव में क्या करना चाहता हूं कि डेटाबेस में सहेजे जाने के बाद साझा कैश में संशोधन डालना है। – Arnaud

उत्तर

8

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

सामान्य स्थिति में, मानक म्यूटेक्स अधिग्रहण किया जाता है और ध्वज केवल एक परमाणु संचालन की लागत के साथ सेट किया जाता है। यदि मानक म्यूटेक्स तुरंत लॉक नहीं किया जा सकता है, तो धागा सो जाएगा।

यदि किसी भी कारण से मानक म्यूटेक्स फेंकता है, तो म्यूटेक्स अपने स्पिन मोड में प्रवेश करेगा। थ्रेड जहां अपवाद हुआ तब लूप होगा जब तक कि यह ध्वज सेट नहीं कर सके। चूंकि कोई अन्य धागा इस बात से अवगत नहीं है कि यह धागा पूरी तरह से मानक म्यूटेक्स से घिरा हुआ है, वे भी स्पिन कर सकते हैं।

सबसे खराब स्थिति परिदृश्य में, यह लॉकिंग तंत्र स्पिन लॉक में गिरावट आती है। अधिकांश समय यह सामान्य म्यूटेक्स की तरह प्रतिक्रिया करता है।

3

यह एक बुरी स्थिति है। आपका विनाशक कुछ ऐसा कर रहा है जो असफल हो सकता है। यदि इस काउंटर को अपडेट करने में विफलता आपके एप्लिकेशन को अपरिवर्तनीय रूप से दूषित कर देगी, तो आप बस विनाशक को फेंकने देना चाहेंगे। यह आपके आवेदन को terminate पर कॉल के साथ दुर्घटनाग्रस्त कर देगा, लेकिन यदि आपका एप्लिकेशन दूषित हो गया है, तो प्रक्रिया को मारना और कुछ उच्च स्तरीय वसूली योजना (जैसे डेमॉन के लिए वॉचडॉग या किसी अन्य उपयोगिता के लिए निष्पादन को पुनः प्रयास करना) पर भरोसा करना बेहतर हो सकता है। । अगर काउंटर को कम करने में विफलता पुनर्प्राप्त करने योग्य है, तो आपको try{}catch() ब्लॉक के साथ अपवाद अवशोषित करना चाहिए और पुनर्प्राप्त करना चाहिए (या संभावित रूप से पुनर्प्राप्ति के लिए किसी अन्य ऑपरेशन के लिए संभावित रूप से जानकारी सहेजना चाहिए)। यदि यह पुनर्प्राप्त करने योग्य नहीं है, लेकिन यह घातक नहीं है, तो आप अपवाद को पकड़ने और अवशोषित करना और विफलता को लॉग करना चाहते हैं (निश्चित रूप से अपवाद-सुरक्षित तरीके से लॉग इन करना सुनिश्चित करना)।

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

class NoThrowMutex{ 
private: 
    std::mutex mutex; 
    std::atomic_flag flag; 
    bool both; 
public: 
    NoThrowMutex(); 
    ~NoThrowMutex(); 
    void lock(); 
    void unlock(); 
}; 

NoThrowMutex::NoThrowMutex():mutex(),flag(),both(false){ 
    flag.clear(std::memory_order_release);} 

NoThrowMutex::~NoThrowMutex(){} 

void NoThrowMutex::lock(){ 
    try{ 
     mutex.lock(); 
     while(flag.test_and_set(std::memory_order_acquire)); 
     both=true;} 
    catch(...){ 
     while(flag.test_and_set(std::memory_order_acquire)); 
     both=false;}} 

void NoThrowMutex::unlock(){ 
    if(both){mutex.unlock();} 
    flag.clear(std::memory_order_release);} 

विचार केवल एक के बजाय दो म्युटेक्स है: