Смекни!
smekni.com

Критические секции (стр. 4 из 4)

Листинг 17. Классы CLock и CScopeLock, вариант для отладки.

class CLock{ friend class CScopeLock; CRITICAL_SECTION m_CS;public: void Init() { ::InitializeCriticalSection(&m_CS); } void Term() { ::DeleteCriticalSection(&m_CS); }#if defined(CS_DEBUG) BOOL Check() { return CheckCriticalSection(&m_CS); }#endif#if CS_DEBUG > 1 void Lock(int nLine, LPSTR azFile) { EnterCriticalSectionDbg(&m_CS, nLine, azFile); } BOOL TryLock(int nLine, LPSTR azFile) { return TryEnterCriticalSectionDbg(&m_CS, nLine, azFile); }#else void Lock() { ::EnterCriticalSection(&m_CS); } BOOL TryLock() { return ::TryEnterCriticalSection(&m_CS); }#endif void Unlock() { ::LeaveCriticalSection(&m_CS); }};class CScopeLock{ LPCRITICAL_SECTION m_pCS;public:#if CS_DEBUG > 1 CScopeLock(LPCRITICAL_SECTION pCS, int nLine, LPSTR azFile) : m_pCS(pCS) { Lock(nLine, azFile); } CScopeLock(CLock& lock, int nLine, LPSTR azFile) : m_pCS(&lock.m_CS) { Lock(nLine, azFile); } void Lock(int nLine, LPSTR azFile) { EnterCriticalSectionDbg(m_pCS, nLine, azFile); }#else CScopeLock(LPCRITICAL_SECTION pCS) : m_pCS(pCS) { Lock(); } CScopeLock(CLock& lock) : m_pCS(&lock.m_CS) { Lock(); } void Lock() { ::EnterCriticalSection(m_pCS); }#endif ~CScopeLock() { Unlock(); } void Unlock() { ::LeaveCriticalSection(m_pCS); }};#if CS_DEBUG > 1#define Lock() Lock(__LINE__, __FILE__)#define TryLock() TryLock(__LINE__, __FILE__)#define lock(cs) lock(cs, __LINE__, __FILE__)#endif

К сожалению, пришлось даже переопределить CScopeLock lock(cs), причем жестко привязаться к имени переменной. Не стоит говорить о том, что наверняка получился конфликт имен - все-таки Lock довольно популярное название для метода. Такой код не будет собираться, например, с популярнейшей библиотекой ATL. Тут есть два способа. Переименовать методы Lock() и TryLock() во что-нибудь более уникальное, либо переименовать Lock() в ATL:

// StdAfx.h//. ..#define Lock ATLLock#include <AtlBase.h>//. ..

Сменим тему

А что это мы все про Win32 API да про C++? Давайте посмотрим, как обстоят дела с критическими секциями в более современных языках программирования.

C#

Тут стараниями Майкрософт имеется полный набор старого доброго API под новыми именами.

Критические секции представлены классом System.Threading.Monitor, вместо ::EnterCriticalSection() есть Monitor.Enter(object), а вместо ::LeaveCriticalSection() Monitor.Exit(object), где object – это любой объект C#. Т.е. каждый объект где-то в потрохах CLR (Common Language Runtime) имеет свою собственную критическую секцию либо заводит ее по необходимости. Типичное использование этой секции выглядит так:

Monitor.Enter(this);m_dwSmth = dwSmth;Monitor.Exit(this);

Если нужно организовать отдельную критическую секцию для какой-либо переменной, самым логичным способом будет поместить ее в отдельный объект и использовать этот объект как аргумент при вызове Monitor.Enter/Exit(). Кроме того, в C# существует ключевое слово lock, это полный аналог нашего класса CScopeLock.

lock (this){ m_dwSmth = dwSmth;}

А вот Monitor.TryEnter() в C# (о, чудо!) принимает в качестве параметра максимальный период ожидания.

Замечу, что CLR – это не только C#, все это применимо и к другим языкам, использующим CLR.

Java

В этом языке используется подобный механизм, только место ключевого слова lock есть ключевое слово synchronized, а все остальное – точно так же.

synchronized (this){ m_dwSmth = dwSmth;}

MC++ (управляемый C++)

Тут тоже появился атрибут [synchronized] ведущий себя точно так же, как и одноименное ключевое слово из Java. Странно, что архитекторы из Майкрософт решили позаимствовать синтаксис из продукта от Sun Microsystems вместо своего собственного.

[synchronized] DWORD m_dwSmth;//...m_dwSmth = dwSmth; // неявныйвызов Lock(this)

Delphi

Практически все, что верно для C++, верно и для Delphi. Критические секции представлены объектом TCriticalSection. Собственно, это такая же обертка как и наш класс CLock.

Кроме того, в Delphi присутствует специальный объект TMultiReadExclusiveWriteSynchronizer с названием, говорящим само за себя.

Подведем итоги

Итак, что нужно знать о критических секциях:

Критические секции работают быстро и не требуют большого количества системных ресурсов.

Для синхронизации доступа к нескольким (независимым) переменным лучше использовать несколько критических секций, а не одну для всех.

Код, ограниченный критическими секциями, лучше всего свести к минимуму.

Находясь в критической секции, не стоит вызывать методы "чужих" объектов.