Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
inline VOID LeaveCriticalSectionDbg(LPCRITICAL_SECTION_DBG pcs) {
// Проверяем, чтобы идентификатор текущей нити совпадал
// с идентификатором нити-влядельца.
// Если это не так, скорее всего мы имеем дело с ошибкой
ATLASSERT(pcs->OwningThread == (HANDLE)::GetCurrentThreadId());
if (--pcs->RecursionCount) {
// Не последний вызов из этой нити.
// Уменьшаем значение поля LockCount
::InterlockedDecrement(&pcs->LockCount);
} else {
// Последний вызов. Нужно "разбудить" какую-либо
// из ожидающих ниток, если таковые имеются
ATLASSERT(NULL != pcs->OwningThread);
pcs->OwningThread = NULL;
pcs->m_nLine = 0;
pcs->m_azFile = NULL;
if (::InterlockedDecrement(&pcs->LockCount) >= 0) {
// Имеется, как минимум, одна ожидающая нить
_UnWaitCriticalSectionDbg(pcs);
}
}
}
// Удостоверяемся, что ::EnterCriticalSection() была вызвана
// до вызова этого метода
inline BOOL CheckCriticalSection(LPCRITICAL_SECTION pcs) {
return pcs->LockCount >= 0
&& pcs->OwningThread == (HANDLE)::GetCurrentThreadId();
}
// Переопределяем все функции для работы с критическими секциями.
// Определение класса CLock должно быть после этих строк
#define InitializeCriticalSection InitializeCriticalSectionDbg
#define InitializeCriticalSectionAndSpinCount(pcs, c)
InitializeCriticalSectionDbg(pcs)
#define DeleteCriticalSection DeleteCriticalSectionDbg
#define EnterCriticalSection(pcs) EnterCriticalSectionDbg(pcs, __LINE__, __FILE__)
#define TryEnterCriticalSection(pcs) TryEnterCriticalSectionDbg(pcs, __LINE__, __FILE__)
#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 с названием, говорящим само за себя.
Подведем итогиИтак, что нужно знать о критических секциях:
• Критические секции работают быстро и не требуют большого количества системных ресурсов.
• Для синхронизации доступа к нескольким (независимым) переменным лучше использовать несколько критических секций, а не одну для всех.
• Код, ограниченный критическими секциями, лучше всего свести к минимуму.
• Находясь в критической секции, не стоит вызовать методы "чужих" объектов.
Это все на сегодня. Пока!
Алекс Jenter [email protected] Duisburg, 2001. Публикуемые в рассылке материалы принадлежат сайту RSDN.Программирование на Visual C++
Выпуск №67 от 10 марта 2002 г.
Здравствуйте, уважаемые подписчики!
Сегодня я хочу предложить вашему вниманию тему, которая еще ни разу не появлялась на страницах рассылки, хотя без сомнения этого заслуживает. Эта тема – графическая библиотека OpenGl, которая уже долгое время является фактическим стандартом для серьезных 3D приложений.
К сожалению из-за большого объема статьи ее пришлось разбить на две части. Но вторую часть вы получите сразу же в выпуске 67б, так что вам не придется ждать целую неделю ;-)
СТАТЬЯ
Учебное пособие по OpenGL
Авторы: Фролов Антон
Игнатенко Алексей
Источник: Лаборатория компьютерной графики при ВМиК МГУ
ВведениеOpenGL является на данный момент одним из самых популярных программных интерфейсов (API) для разработки приложений в области двумерной и трехмерной графики. Стандарт OpenGL был разработан и утвержден в 1992 году ведущими фирмами в области разработки программного обеспечения, а его основой стала библиотека IRIS GL, разработанная Silicon Graphics.
На данный момент реализация OpenGL включает в себя несколько библиотек (описание базовых функций OpenGL, GLU,GLUT,GLAUX и другие), назначение которых будет описано ниже.
Что такое OpenGL?
С точки зрения программиста, OpenGL – это набор команд, которые описывают геометрические объекты и способ их отображения на экране. В большинстве случаев OpenGL предоставляет непосредственный интерфейс, т.е. определение объекта вызывает его визуализацию в буфере кадра.
Типичная программа, использующая OpenGL, начинается с определения окна, в котором будет происходить отображение. Затем создается контекст OpenGL и ассоциируется с этим окном. Далее программист может свободно использовать команды и операции OpenGL API. Часть команд используются для рисования простых геометрических объектов (т.е. точек, линий, многоугольников), тогда как другие задают режимы отображения этих примитивов. Например, можно задать режимы заливки цветом, отображение из трехмерной системы координат в экранную систему. Есть возможности для прямого контроля над буфером кадра, такие как чтение и запись пикселей.