खोज…
टिप्पणियों
एसटीडी का उपयोग करना बेहतर है :: एसटीडी की तुलना में शेयर्ड_म्यूटेक्स :: साझा किया गया ।
प्रदर्शन का अंतर डबल से अधिक है।
यदि आप RWLock का उपयोग करना चाहते हैं, तो आप पाएंगे कि दो विकल्प हैं।
यह std :: shared_mutex और साझा_timed_mutex है।
आप सोच सकते हैं कि std :: share_timed_mutex केवल संस्करण 'std :: साझा_म्यूटेक्स + टाइम विधि' है।
लेकिन कार्यान्वयन पूरी तरह से अलग है।
नीचे दिए गए कोड MSVC14.1 std का कार्यान्वयन :: साझा_म्यूटेक्स है।
class shared_mutex
{
public:
typedef _Smtx_t * native_handle_type;
shared_mutex() _NOEXCEPT
: _Myhandle(0)
{ // default construct
}
~shared_mutex() _NOEXCEPT
{ // destroy the object
}
void lock() _NOEXCEPT
{ // lock exclusive
_Smtx_lock_exclusive(&_Myhandle);
}
bool try_lock() _NOEXCEPT
{ // try to lock exclusive
return (_Smtx_try_lock_exclusive(&_Myhandle) != 0);
}
void unlock() _NOEXCEPT
{ // unlock exclusive
_Smtx_unlock_exclusive(&_Myhandle);
}
void lock_shared() _NOEXCEPT
{ // lock non-exclusive
_Smtx_lock_shared(&_Myhandle);
}
bool try_lock_shared() _NOEXCEPT
{ // try to lock non-exclusive
return (_Smtx_try_lock_shared(&_Myhandle) != 0);
}
void unlock_shared() _NOEXCEPT
{ // unlock non-exclusive
_Smtx_unlock_shared(&_Myhandle);
}
native_handle_type native_handle() _NOEXCEPT
{ // get native handle
return (&_Myhandle);
}
shared_mutex(const shared_mutex&) = delete;
shared_mutex& operator=(const shared_mutex&) = delete;
private:
_Smtx_t _Myhandle;
};
void __cdecl _Smtx_lock_exclusive(_Smtx_t * smtx)
{ /* lock shared mutex exclusively */
AcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(smtx));
}
void __cdecl _Smtx_lock_shared(_Smtx_t * smtx)
{ /* lock shared mutex non-exclusively */
AcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(smtx));
}
int __cdecl _Smtx_try_lock_exclusive(_Smtx_t * smtx)
{ /* try to lock shared mutex exclusively */
return (TryAcquireSRWLockExclusive(reinterpret_cast<PSRWLOCK>(smtx)));
}
int __cdecl _Smtx_try_lock_shared(_Smtx_t * smtx)
{ /* try to lock shared mutex non-exclusively */
return (TryAcquireSRWLockShared(reinterpret_cast<PSRWLOCK>(smtx)));
}
void __cdecl _Smtx_unlock_exclusive(_Smtx_t * smtx)
{ /* unlock exclusive shared mutex */
ReleaseSRWLockExclusive(reinterpret_cast<PSRWLOCK>(smtx));
}
void __cdecl _Smtx_unlock_shared(_Smtx_t * smtx)
{ /* unlock non-exclusive shared mutex */
ReleaseSRWLockShared(reinterpret_cast<PSRWLOCK>(smtx));
}
आप देख सकते हैं कि std :: shared_mutex विंडोज स्लिम रीडर / राइट लॉक ( https://msdn.microsoft.com/ko-kr/library/windows/desktop/aa904937(v=vs.854)aspasp) में लागू किया गया है
अब आइए std के कार्यान्वयन को देखें :: share_timed_mutex।
नीचे दिए गए कोड MSVC14.1 एसटीडी का कार्यान्वयन :: साझा_timed_mutex है।
class shared_timed_mutex
{
typedef unsigned int _Read_cnt_t;
static constexpr _Read_cnt_t _Max_readers = _Read_cnt_t(-1);
public:
shared_timed_mutex() _NOEXCEPT
: _Mymtx(), _Read_queue(), _Write_queue(),
_Readers(0), _Writing(false)
{ // default construct
}
~shared_timed_mutex() _NOEXCEPT
{ // destroy the object
}
void lock()
{ // lock exclusive
unique_lock<mutex> _Lock(_Mymtx);
while (_Writing)
_Write_queue.wait(_Lock);
_Writing = true;
while (0 < _Readers)
_Read_queue.wait(_Lock); // wait for writing, no readers
}
bool try_lock()
{ // try to lock exclusive
lock_guard<mutex> _Lock(_Mymtx);
if (_Writing || 0 < _Readers)
return (false);
else
{ // set writing, no readers
_Writing = true;
return (true);
}
}
template<class _Rep,
class _Period>
bool try_lock_for(
const chrono::duration<_Rep, _Period>& _Rel_time)
{ // try to lock for duration
return (try_lock_until(chrono::steady_clock::now() + _Rel_time));
}
template<class _Clock,
class _Duration>
bool try_lock_until(
const chrono::time_point<_Clock, _Duration>& _Abs_time)
{ // try to lock until time point
auto _Not_writing = [this] { return (!_Writing); };
auto _Zero_readers = [this] { return (_Readers == 0); };
unique_lock<mutex> _Lock(_Mymtx);
if (!_Write_queue.wait_until(_Lock, _Abs_time, _Not_writing))
return (false);
_Writing = true;
if (!_Read_queue.wait_until(_Lock, _Abs_time, _Zero_readers))
{ // timeout, leave writing state
_Writing = false;
_Lock.unlock(); // unlock before notifying, for efficiency
_Write_queue.notify_all();
return (false);
}
return (true);
}
void unlock()
{ // unlock exclusive
{ // unlock before notifying, for efficiency
lock_guard<mutex> _Lock(_Mymtx);
_Writing = false;
}
_Write_queue.notify_all();
}
void lock_shared()
{ // lock non-exclusive
unique_lock<mutex> _Lock(_Mymtx);
while (_Writing || _Readers == _Max_readers)
_Write_queue.wait(_Lock);
++_Readers;
}
bool try_lock_shared()
{ // try to lock non-exclusive
lock_guard<mutex> _Lock(_Mymtx);
if (_Writing || _Readers == _Max_readers)
return (false);
else
{ // count another reader
++_Readers;
return (true);
}
}
template<class _Rep,
class _Period>
bool try_lock_shared_for(
const chrono::duration<_Rep, _Period>& _Rel_time)
{ // try to lock non-exclusive for relative time
return (try_lock_shared_until(_Rel_time
+ chrono::steady_clock::now()));
}
template<class _Time>
bool _Try_lock_shared_until(_Time _Abs_time)
{ // try to lock non-exclusive until absolute time
auto _Can_acquire = [this] {
return (!_Writing && _Readers < _Max_readers); };
unique_lock<mutex> _Lock(_Mymtx);
if (!_Write_queue.wait_until(_Lock, _Abs_time, _Can_acquire))
return (false);
++_Readers;
return (true);
}
template<class _Clock,
class _Duration>
bool try_lock_shared_until(
const chrono::time_point<_Clock, _Duration>& _Abs_time)
{ // try to lock non-exclusive until absolute time
return (_Try_lock_shared_until(_Abs_time));
}
bool try_lock_shared_until(const xtime *_Abs_time)
{ // try to lock non-exclusive until absolute time
return (_Try_lock_shared_until(_Abs_time));
}
void unlock_shared()
{ // unlock non-exclusive
_Read_cnt_t _Local_readers;
bool _Local_writing;
{ // unlock before notifying, for efficiency
lock_guard<mutex> _Lock(_Mymtx);
--_Readers;
_Local_readers = _Readers;
_Local_writing = _Writing;
}
if (_Local_writing && _Local_readers == 0)
_Read_queue.notify_one();
else if (!_Local_writing && _Local_readers == _Max_readers - 1)
_Write_queue.notify_all();
}
shared_timed_mutex(const shared_timed_mutex&) = delete;
shared_timed_mutex& operator=(const shared_timed_mutex&) = delete;
private:
mutex _Mymtx;
condition_variable _Read_queue, _Write_queue;
_Read_cnt_t _Readers;
bool _Writing;
};
class stl_condition_variable_win7 final : public stl_condition_variable_interface
{
public:
stl_condition_variable_win7()
{
__crtInitializeConditionVariable(&m_condition_variable);
}
~stl_condition_variable_win7() = delete;
stl_condition_variable_win7(const stl_condition_variable_win7&) = delete;
stl_condition_variable_win7& operator=(const stl_condition_variable_win7&) = delete;
virtual void destroy() override {}
virtual void wait(stl_critical_section_interface *lock) override
{
if (!stl_condition_variable_win7::wait_for(lock, INFINITE))
std::terminate();
}
virtual bool wait_for(stl_critical_section_interface *lock, unsigned int timeout) override
{
return __crtSleepConditionVariableSRW(&m_condition_variable, static_cast<stl_critical_section_win7 *>(lock)->native_handle(), timeout, 0) != 0;
}
virtual void notify_one() override
{
__crtWakeConditionVariable(&m_condition_variable);
}
virtual void notify_all() override
{
__crtWakeAllConditionVariable(&m_condition_variable);
}
private:
CONDITION_VARIABLE m_condition_variable;
};
आप देख सकते हैं कि std :: share_timed_mutex को std :: condition_value में लागू किया गया है।
यह बहुत बड़ा अंतर है।
तो आइए उनमें से दो के प्रदर्शन की जांच करें।
यह 1000 मिलीसेकंड के लिए पढ़ने / लिखने के परीक्षण का परिणाम है।
std :: share_mutex संसाधित पढ़ने / लिखने से 2 गुना अधिक std :: share_timed_mutex।
इस उदाहरण में, रीड / राइट रेशियो समान है, लेकिन रीड रेट वास्तविक में राइट रेट से अधिक है।
इसलिए, प्रदर्शन अंतर बड़ा हो सकता है।
इस उदाहरण में नीचे दिया गया कोड है।
void useSTLSharedMutex()
{
std::shared_mutex shared_mtx_lock;
std::vector<std::thread> readThreads;
std::vector<std::thread> writeThreads;
std::list<int> data = { 0 };
volatile bool exit = false;
std::atomic<int> readProcessedCnt(0);
std::atomic<int> writeProcessedCnt(0);
for (unsigned int i = 0; i < std::thread::hardware_concurrency(); i++)
{
readThreads.push_back(std::thread([&data, &exit, &shared_mtx_lock, &readProcessedCnt]() {
std::list<int> mydata;
int localProcessCnt = 0;
while (true)
{
shared_mtx_lock.lock_shared();
mydata.push_back(data.back());
++localProcessCnt;
shared_mtx_lock.unlock_shared();
if (exit)
break;
}
std::atomic_fetch_add(&readProcessedCnt, localProcessCnt);
}));
writeThreads.push_back(std::thread([&data, &exit, &shared_mtx_lock, &writeProcessedCnt]() {
int localProcessCnt = 0;
while (true)
{
shared_mtx_lock.lock();
data.push_back(rand() % 100);
++localProcessCnt;
shared_mtx_lock.unlock();
if (exit)
break;
}
std::atomic_fetch_add(&writeProcessedCnt, localProcessCnt);
}));
}
std::this_thread::sleep_for(std::chrono::milliseconds(MAIN_WAIT_MILLISECONDS));
exit = true;
for (auto &r : readThreads)
r.join();
for (auto &w : writeThreads)
w.join();
std::cout << "STLSharedMutex READ : " << readProcessedCnt << std::endl;
std::cout << "STLSharedMutex WRITE : " << writeProcessedCnt << std::endl;
std::cout << "TOTAL READ&WRITE : " << readProcessedCnt + writeProcessedCnt << std::endl << std::endl;
}
void useSTLSharedTimedMutex()
{
std::shared_timed_mutex shared_mtx_lock;
std::vector<std::thread> readThreads;
std::vector<std::thread> writeThreads;
std::list<int> data = { 0 };
volatile bool exit = false;
std::atomic<int> readProcessedCnt(0);
std::atomic<int> writeProcessedCnt(0);
for (unsigned int i = 0; i < std::thread::hardware_concurrency(); i++)
{
readThreads.push_back(std::thread([&data, &exit, &shared_mtx_lock, &readProcessedCnt]() {
std::list<int> mydata;
int localProcessCnt = 0;
while (true)
{
shared_mtx_lock.lock_shared();
mydata.push_back(data.back());
++localProcessCnt;
shared_mtx_lock.unlock_shared();
if (exit)
break;
}
std::atomic_fetch_add(&readProcessedCnt, localProcessCnt);
}));
writeThreads.push_back(std::thread([&data, &exit, &shared_mtx_lock, &writeProcessedCnt]() {
int localProcessCnt = 0;
while (true)
{
shared_mtx_lock.lock();
data.push_back(rand() % 100);
++localProcessCnt;
shared_mtx_lock.unlock();
if (exit)
break;
}
std::atomic_fetch_add(&writeProcessedCnt, localProcessCnt);
}));
}
std::this_thread::sleep_for(std::chrono::milliseconds(MAIN_WAIT_MILLISECONDS));
exit = true;
for (auto &r : readThreads)
r.join();
for (auto &w : writeThreads)
w.join();
std::cout << "STLSharedTimedMutex READ : " << readProcessedCnt << std::endl;
std::cout << "STLSharedTimedMutex WRITE : " << writeProcessedCnt << std::endl;
std::cout << "TOTAL READ&WRITE : " << readProcessedCnt + writeProcessedCnt << std::endl << std::endl;
}
std :: unique_lock, std :: share_lock, std :: lock_guard
आरएआई शैली के लिए प्रयोग किया जाता है कोशिश ताले, समयबद्ध कोशिश ताले और पुनरावर्ती ताले।
std::unique_lock
के विशेष स्वामित्व के लिए अनुमति देता है।
std::shared_lock
के साझा स्वामित्व की अनुमति देता है। कई सूत्र पकड़ कर सकते हैं std::shared_locks
एक पर std::shared_mutex
। C ++ 14 से उपलब्ध है।
std::lock_guard
लिए एक हल्का विकल्प है std::unique_lock
और std::shared_lock
।
#include <unordered_map>
#include <mutex>
#include <shared_mutex>
#include <thread>
#include <string>
#include <iostream>
class PhoneBook {
public:
std::string getPhoneNo( const std::string & name )
{
std::shared_lock<std::shared_timed_mutex> l(_protect);
auto it = _phonebook.find( name );
if ( it != _phonebook.end() )
return (*it).second;
return "";
}
void addPhoneNo ( const std::string & name, const std::string & phone )
{
std::unique_lock<std::shared_timed_mutex> l(_protect);
_phonebook[name] = phone;
}
std::shared_timed_mutex _protect;
std::unordered_map<std::string,std::string> _phonebook;
};
लॉक क्लासेस के लिए रणनीतियाँ: std :: try_to_lock, std :: adop_lock, std :: defer_lock
जब एक std :: unique_lock बनाते हैं, तो चुनने के लिए तीन अलग-अलग लॉकिंग रणनीतियाँ होती हैं: std::try_to_lock
, std::defer_lock
और std::adopt_lock
-
std::try_to_lock
बिना किसी अवरोध के लॉक कोstd::try_to_lock
अनुमति देता है:
{
std::atomic_int temp {0};
std::mutex _mutex;
std::thread t( [&](){
while( temp!= -1){
std::this_thread::sleep_for(std::chrono::seconds(5));
std::unique_lock<std::mutex> lock( _mutex, std::try_to_lock);
if(lock.owns_lock()){
//do something
temp=0;
}
}
});
while ( true )
{
std::this_thread::sleep_for(std::chrono::seconds(1));
std::unique_lock<std::mutex> lock( _mutex, std::try_to_lock);
if(lock.owns_lock()){
if (temp < INT_MAX){
++temp;
}
std::cout << temp << std::endl;
}
}
}
-
std::defer_lock
लॉक को प्राप्त किए बिना लॉक संरचना बनाने की अनुमति देता है। जब एक से अधिक म्यूटेक्स को लॉक किया जाता है, तो गतिरोध के लिए अवसर की एक खिड़की होती है यदि दो फ़ंक्शन कॉलर्स एक ही समय में ताले प्राप्त करने का प्रयास करते हैं:
{
std::unique_lock<std::mutex> lock1(_mutex1, std::defer_lock);
std::unique_lock<std::mutex> lock2(_mutex2, std::defer_lock);
lock1.lock()
lock2.lock(); // deadlock here
std::cout << "Locked! << std::endl;
//...
}
निम्नलिखित कोड के साथ, फ़ंक्शन में जो कुछ भी होता है, ताले अधिग्रहित किए जाते हैं और उपयुक्त क्रम में जारी किए जाते हैं:
{
std::unique_lock<std::mutex> lock1(_mutex1, std::defer_lock);
std::unique_lock<std::mutex> lock2(_mutex2, std::defer_lock);
std::lock(lock1,lock2); // no deadlock possible
std::cout << "Locked! << std::endl;
//...
}
-
std::adopt_lock
दूसरी बार लॉक करने का प्रयास नहीं करता है यदि कॉलिंग थ्रेड वर्तमान में लॉक का मालिक है।
{
std::unique_lock<std::mutex> lock1(_mutex1, std::adopt_lock);
std::unique_lock<std::mutex> lock2(_mutex2, std::adopt_lock);
std::cout << "Locked! << std::endl;
//...
}
कुछ ध्यान में रखना है कि std :: adop_lock पुनरावर्ती mutex उपयोग के लिए एक विकल्प नहीं है। जब ताला दायरे से बाहर चला जाता है म्युटेक्स जारी किया गया है।
std :: म्युटेक्स
std :: mutex एक सरल, गैर-पुनरावर्ती सिंक्रनाइज़ेशन संरचना है जिसका उपयोग डेटा की सुरक्षा के लिए किया जाता है जो कि कई थ्रेड्स द्वारा एक्सेस किया जाता है।
std::atomic_int temp{0};
std::mutex _mutex;
std::thread t( [&](){
while( temp!= -1){
std::this_thread::sleep_for(std::chrono::seconds(5));
std::unique_lock<std::mutex> lock( _mutex);
temp=0;
}
});
while ( true )
{
std::this_thread::sleep_for(std::chrono::milliseconds(1));
std::unique_lock<std::mutex> lock( _mutex, std::try_to_lock);
if ( temp < INT_MAX )
temp++;
cout << temp << endl;
}
std :: scoped_lock (C ++ 17)
std::scoped_lock
एक और म्यूटेक्स के मालिक के लिए RAII शैली शब्दार्थ प्रदान करता है, जिसे std::lock
द्वारा उपयोग किए जाने वाले लॉक अवॉइड एल्गोरिदम के साथ जोड़ा जाता है। जब std::scoped_lock
नष्ट हो जाता है, तो म्यूटेक्स को उल्टे क्रम में छोड़ा जाता है, जहाँ से वे अधिग्रहित होते हैं।
{
std::scoped_lock lock{_mutex1,_mutex2};
//do something
}
म्यूटेक्स प्रकार
C ++ 1x म्यूटेक्स कक्षाओं का चयन प्रदान करता है:
- std :: mutex - सरल लॉकिंग कार्यक्षमता प्रदान करता है।
- std :: timed_mutex - try_to_lock कार्यक्षमता प्रदान करता है
- std :: recursive_mutex - एक ही थ्रेड द्वारा पुनरावर्ती लॉकिंग की अनुमति देता है।
- std :: share_mutex, std :: share_timed_mutex - साझा और अद्वितीय लॉक कार्यक्षमता प्रदान करता है।
std :: ताला
std::lock
एक या अधिक म्यूटेक्स को लॉक करने के लिए डेडलॉक परिहार एल्गोरिदम का उपयोग करता है। यदि एक ऑब्जेक्ट को कई ऑब्जेक्ट्स को लॉक करने के लिए कॉल के दौरान फेंक दिया जाता है, तो std::lock
अपवाद को फिर से फेंकने से पहले सफलतापूर्वक लॉक किए गए ऑब्जेक्ट्स को अनलॉक कर देता है।
std::lock(_mutex1, _mutex2);