Чтение онлайн

ЖАНРЫ

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

#define LeaveCriticalSection LeaveCriticalSectionDbg

#define CRITICAL_SECTION CRITICAL_SECTION_DBG

#define LPCRITICAL_SECTION LPCRITICAL_SECTION_DBG

#define PCRITICAL_SECTION PCRITICAL_SECTION_DBG

#endif

Приводим наши классы в соответствие

Листинг 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 с названием, говорящим само за себя.

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

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

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

Поделиться с друзьями: