class critical_section { CRITICAL_SECTION cs_; public: critical_section() { InitializeCriticalSection(&cs_); } ~critical_section() { DeleteCriticalSection(&cs_); } void enter() { EnterCriticalSection(&cs_); } void leave() { LeaveCriticalSection(&cs_); } }; template< class TLock, void (TLock::*en)() = &TLock::enter, void (TLock::*le)() = &TLock::leave > class scoped_lock { TLock& lock_; public: scoped_lock(TLock& lock) : lock_(lock) { (lock_.*en)(); } ~scoped_lock() { (lock_.*le)(); } }; typedef scoped_lock<critical_section> slock;Класс critical_section - критическая секция. Класс scoped_lock - реализует принцип RAII, для произвольного класса. Указатели на методы вынесены в параметры шаблона для того, что-бы можно было работать с более сложными объектами синхронизации, например реализующими read-write lock. Используется это очень просто:
critical_section lock_; void foo () { slock lockit(lock_); //этот участок кода //может выполняться только //одним потоком }в общем, эта идиома широко известна... Далее мне нужно просто использовать этот прием во всех не константных методах класса. Если так сделать, то объект класса сможет выступать в качестве общего ресурса для нескольких потоков. Но здесь есть один существенный минус. Это сделает вызовы методов нашего класса более дорогими, даже, если объект будет использоваться только одним потоком. Что-бы этого избежать, нужно иметь возможность отключать синхронизацию.
Самый простой способ этого добиться - использовать препроцессор, гибкость нулевая, зато работает =) Более правильный способ - не хардкодить critical_section в качестве объекта синхронизации для нашего класса, а передавать его в качестве шаблонного параметра. Если нужно отключить блокировку, достаточно передать в шаблон класс - заглушку, с пустыми методами enter и leave. По сравнению с первым способом это очень гибко, можно в одном месте программы использовать потокобезопасную версию объекта, а в другой небезопасную, но более быструю. Но здесь то-же есть минус. Клиент нашего класса должен знать о том, что должен из себя представлять класс critical_section, это не очень хорошо... Оптимальный вариант с точки зрения чистоты и ясности кода - передавать в шаблон логическое значение. Что-то вроде этого:
template < bool enable_lock = false > class some_class...если передали true, то использовать потокобезопасную версию объекта, если false - обычную. Теперь о том как это реализовать:
template<class TLock, class TScopedLock, bool enabled> struct switch_lock; template<class TLock, class TScopedLock> struct switch_lock<TLock, TScopedLock, true> { TScopedLock lock_it_; switch_lock(TLock& lock) : lock_it_(lock) {} }; template<class TLock, class TScopedLock> struct switch_lock<TLock, TScopedLock, false> { switch_lock(TLock& lock) {} };Выглядит немного пугающе, но это всего-лишь враппер для scoped_lock, в качестве первого параметра он получает класс синхронизации, например critical_section, второй параметр - RAII враппер для первого параметра, например slock, ну и последний параметр отвечает за выбор реализации. Сам класс будет выглядеть так:
template < bool enable_lock = false > class some_class { typedef switch_lock<critical_section, slock, enable_lock> locker; critical_section lock_; public: some_class() { std::cout << "ctor" << std::endl; } template<bool el> some_class(const some_class<el>& el) { std::cout << "copy ctor" << std::endl; } template<bool el> some_class& operator = (const some_class<el>& el) { std::cout << "assign" << std::endl; return *this; } void some_function() { locker l(lock_);//лочим (захватываем мьютэкс, входим в критическую секцию, итд) std::cout << "function called" << std::endl; } };весь трюк состоит в том, что-бы вместо scoped_lock, использовать враппер, вот и все :)