2012-09-28 17 views
12

मैं यह सुनिश्चित करना चाहता हूं कि एक समय में केवल एक थ्रेड मेरे सी ++ वर्ग की विधि चला सकता है। दूसरे शब्दों में, कक्षा को Monitor की तरह व्यवहार करें।एक सी ++ कक्षा को एक मॉनिटर बनाना (समवर्ती अर्थ में)

क्या ऐसा करने के लिए कोई पैटर्न, templatized तरीका है, या कुछ बूस्ट वर्ग मैं उपयोग कर सकते हैं? चूंकि मेरा एकमात्र विचार अब तक एक गंभीर अनुभाग सदस्य जोड़ रहा है, और इसे प्रत्येक विधि की शुरुआत में प्राप्त कर लेता है और अंत में इसे जारी करता है (निश्चित रूप से आरएआईआई का उपयोग करके)। लेकिन यह बहुत अनावश्यक लगता है, और मैं इसे किसी अन्य वर्ग के लिए पुन: उपयोग नहीं कर सकता।

+0

यह मुझे कम कोड के साथ ऐसा करने की कल्पना करना मुश्किल है। आपके प्रस्तावित समाधान के लिए आवश्यक कोड क्लास (म्यूटेक्स सदस्य) और प्रत्येक फ़ंक्शन के लिए एक पंक्ति के लिए एक पंक्ति है। मुझे नहीं लगता कि यह विशिष्ट भाषा समर्थन के बिना छोटा हो जाएगा। क्या यह आपका सवाल है? यदि ऐसा है, तो जवाब है "मॉनिटर बनाने के लिए सी ++ में भाषा समर्थन नहीं है"। :) –

+1

आप एक म्यूटेक्स का उपयोग क्यों नहीं करते हैं, यह व्यापक रूप से उपलब्ध है। अपने स्वयं के सिंक्रनाइज़ेशन प्राइमेटिव्स को खोजना बुरी तरह से बुझाने के लिए एक नाटक है। –

+0

देखें [बूस्ट। थ्रेड] (http://www.boost.org/libs/thread/) यदि वह पर्याप्त रूप से स्पष्ट नहीं था। : -] – ildjarn

उत्तर

4

आप operator-> और आधुनिक C++ जो पहले से स्वीकार किए जाते हैं जवाब की तुलना में बहुत क्लीनर वाक्य रचना के लिए देता है के कुछ विवेकपूर्ण उपयोग के साथ इस लक्ष्य को हासिल कर सकते हैं:

template<class T> 
class monitor 
{ 
public: 
    template<typename ...Args> 
    monitor(Args&&... args) : m_cl(std::forward<Args>(args)...){} 

    struct monitor_helper 
    { 
     monitor_helper(monitor* mon) : m_mon(mon), m_ul(mon->m_lock) {} 
     T* operator->() { return &m_mon->m_cl;} 
     monitor* m_mon; 
     std::unique_lock<std::mutex> m_ul; 
    }; 

    monitor_helper operator->() { return monitor_helper(this); } 
    monitor_helper ManuallyLock() { return monitor_helper(this); } 
    T& GetThreadUnsafeAccess() { return m_cl; } 

private: 
    T   m_cl; 
    std::mutex m_lock; 
}; 

विचार है कि आप तीर ऑपरेटर का उपयोग तक पहुँचने के लिए है अंतर्निहित वस्तु, लेकिन यह एक सहायक ऑब्जेक्ट देता है जो ताले लगाता है और फिर आपके फ़ंक्शन कॉल के आस-पास म्यूटेक्स को अनलॉक करता है। फिर भाषा के जादू के माध्यम से बार-बार operator-> लागू करने के माध्यम से आप अंतर्निहित वस्तु का संदर्भ प्राप्त करते हैं।

उपयोग:

monitor<std::vector<int>> threadSafeVector {5}; 

threadSafeVector->push_back(0); 
threadSafeVector->push_back(1); 
threadSafeVector->push_back(2); 

// Create a bunch of threads that hammer the vector 
std::vector<std::thread> threads; 
for(int i=0; i<16; ++i) 
{ 
    threads.push_back(std::thread([&]() 
    { 
     for(int i=0; i<1024; ++i) 
     { 
      threadSafeVector->push_back(i); 
     } 
    })); 
} 

// You can explicitely take a lock then call multiple functions 
// without the overhead of a relock each time. The 'lock handle' 
// destructor will unlock the lock correctly. This is necessary 
// if you want a chain of logically connected operations 
{ 
    auto lockedHandle = threadSafeVector.ManuallyLock(); 
    if(!lockedHandle->empty()) 
    { 
     lockedHandle->pop_back(); 
     lockedHandle->push_back(-3); 
    } 
} 

for(auto& t : threads) 
{ 
    t.join(); 
} 

// And finally access the underlying object in a raw fashion without a lock 
// Use with Caution! 

std::vector<int>& rawVector = threadSafeVector.GetThreadUnsafeAccess(); 
rawVector.push_back(555); 

// Should be 16393 (5+3+16*1024+1) 
std::cout << threadSafeVector->size() << std::endl; 
11

पहले जेनेरिक मॉनीटर क्लास बनाएं। के सी ++ 11 शक्ति के साथ आप इसे के रूप में इस के रूप में सरल कर सकते हैं:

template <class F> 
struct FunctionType; 
template <class R, class Object, class... Args> 
struct FunctionType<R (Object::*)(Args...)> { 
    typedef R return_type; 
}; 
template <class R, class Object, class... Args> 
struct FunctionType<R (Object::*)(Args...) const> { 
    typedef R return_type; 
}; 

template <class Object_> 
class Monitor { 
public: 
    typedef Object_ object_type; 
    template <class F, class... Args > 
    typename FunctionType<F>::return_type operation(const F& f, Args... args) 
    { 
     critical_section cs; 
     return (object.*f)(args...); 
    } 
    template <class F, class... Args > 
    typename FunctionType<F>::return_type operation(const F& f, Args... args) const 
    { 
     critical_section cs; 
     return (object.*f)(args...); 
    } 
private: 
    object_type object; 
    class critical_section {}; 
}; 
बेशक

critical_section कार्यान्वयन आप पर निर्भर है। मैं पॉज़िक्स या कुछ बूस्ट की सलाह देता हूं।

यह अभी उपयोग करने के लिए तैयार है:

Monitor<std::vector<int> > v; 
v.operation((void (std::vector<int>::*)(const int&)) &std::vector<int>::push_back, 1); 
v.operation((void (std::vector<int>::*)(const int&)) &std::vector<int>::push_back, 2); 
size = v.operation(&std::vector<int>::size); 
std::cout << size << std::endl; 

आप देख सकते हैं कभी कभी आप स्पष्ट रूप से राज्य के लिए आप कॉल करना चाहते जो सदस्य समारोह की आवश्यकता होगी - std :: वेक्टर <> एक से अधिक push_back है ...


compilers जो अभी भी variadic टेम्पलेट का समर्थन नहीं करते के लिए - यह नीचे के बिना समाधान - यदि आवश्यक - - यह बहुत असुविधाजनक है - मैं समय के लिए दो तर्क पर निर्भर है और अधिक तर्क के साथ समारोह जोड़ें:

template <class F> 
struct FunctionType; 
template <class R, class Object> 
struct FunctionType<R (Object::*)()> { 
    typedef R return_type; 
}; 
template <class R, class Object> 
struct FunctionType<R (Object::*)() const> { 
    typedef R return_type; 
}; 
template <class R, class Object, class Arg1> 
struct FunctionType<R (Object::*)(Arg1)> { 
    typedef R return_type; 
}; 
template <class R, class Object, class Arg1> 
struct FunctionType<R (Object::*)(Arg1) const> { 
    typedef R return_type; 
}; 
template <class R, class Object, class Arg1, class Arg2> 
struct FunctionType<R (Object::*)(Arg1,Arg2)> { 
    typedef R return_type; 
}; 
template <class R, class Object, class Arg1, class Arg2> 
struct FunctionType<R (Object::*)(Arg1,Arg2) const> { 
    typedef R return_type; 
}; 

template <class Object_> 
class Monitor { 
public: 
    typedef Object_ object_type; 
    template <class F> 
    typename FunctionType<F>::return_type operation(const F& f) 
    { 
     critical_section cs; 
     return (object.*f)(); 
    } 
    template <class F> 
    typename FunctionType<F>::return_type operation(const F& f) const 
    { 
     critical_section cs; 
     return (object.*f)(); 
    } 
    template <class F, class Arg1> 
    typename FunctionType<F>::return_type operation(const F& f, Arg1 arg1) 
    { 
     critical_section cs; 
     return (object.*f)(arg1); 
    } 
    template <class F, class Arg1> 
    typename FunctionType<F>::return_type operation(const F& f, Arg1 arg1) const 
    { 
     critical_section cs; 
     return (object.*f)(arg1); 
    } 
    template <class F, class Arg1, class Arg2> 
    typename FunctionType<F>::return_type operation(const F& f, Arg1 arg1, Arg2 arg2) 
    { 
     critical_section cs; 
     return (object.*f)(arg1, arg2); 
    } 
    template <class F, class Arg1, class Arg2> 
    typename FunctionType<F>::return_type operation(const F& f, Arg1 arg1, Arg2 arg2) const 
    { 
     critical_section cs; 
     return (object.*f)(arg1, arg2); 
    } 
private: 
    object_type object; 
    class critical_section {}; 
}; 
+0

यह एक बहुत ही सुरुचिपूर्ण समाधान है; केवल नकारात्मक पक्ष यह है कि इसे विविध टेम्पलेट समर्थन की आवश्यकता होती है, जिसे वीसी ++ में अभी भी कमी है। –

+1

@dario_ramos एक बार एक बार सी ++ में कोई भिन्न टेम्पलेट नहीं था;) आप ओवरराइड विधियों का उपयोग कर सकते हैं: 'ऑपरेशन' 0,1,2,3 के साथ ... तर्कों की बजाय किसी भी' ऑपरेशन 'के बजाय तर्क। मान लें कि कोई भी 10 से अधिक तर्कों का उपयोग नहीं करता है, जो लागू करना संभव है - हालांकि विविध टेम्पलेट्स से अधिक परेशानी ... – PiotrNycz

+0

यह एक बहुत साफ समाधान है। मुझे नहीं लगता कि आप एक पुस्तक या आलेख की सिफारिश कर सकते हैं जो विविधता वाले टेम्पलेट्स के उपयोग को और अधिक समझा सकता है? मुझे स्ट्रक्चर फ़ंक्शन टाइप <आर (ऑब्जेक्ट :: *) (Args ...)> हार्ड टू फाथॉम (विशेष रूप से ऑब्जेक्ट :: * बिट।) –