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

Тут можно читать онлайн Алекс Jenter - Программирование на Visual C++. Архив рассылки - бесплатно полную версию книги (целиком) без сокращений. Жанр: comp-programming. Здесь Вы можете читать полную версию (весь текст) онлайн без регистрации и SMS на сайте лучшей интернет библиотеки ЛибКинг или прочесть краткое содержание (суть), предисловие и аннотацию. Так же сможете купить и скачать торрент в электронном формате fb2, найти и слушать аудиокнигу на русском языке или узнать сколько частей в серии и всего страниц в публикации. Читателям доступно смотреть обложку, картинки, описание и отзывы (комментарии) о произведении.
  • Название:
    Программирование на Visual C++. Архив рассылки
  • Автор:
  • Жанр:
  • Издательство:
    неизвестно
  • Год:
    неизвестен
  • ISBN:
    нет данных
  • Рейтинг:
    4.33/5. Голосов: 91
  • Избранное:
    Добавить в избранное
  • Отзывы:
  • Ваша оценка:
    • 80
    • 1
    • 2
    • 3
    • 4
    • 5

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

Программирование на Visual C++. Архив рассылки - описание и краткое содержание, автор Алекс Jenter, читайте бесплатно онлайн на сайте электронной библиотеки LibKing.Ru

РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.

Программирование на Visual C++. Архив рассылки - читать онлайн бесплатно полную версию (весь текст целиком)

Программирование на Visual C++. Архив рассылки - читать книгу онлайн бесплатно, автор Алекс Jenter
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать

И, наконец, поле SpinCount. Это поле используется только многопроцессорными системами. В однопроцессорных системах, если критическая секция занята другой нитью, можно только переключить управление на нее и подождать наступления нашего события. В многопроцессорных системах есть альтернатива: прогнать некоторое количество раз холостой цикл, проверяя каждый раз, не освободилась ли наша критическая секция. Если за SpinCount раз это не получилось, переходим к ожиданию. Это гораздо эффективнее, чем переключение на планировщик ядра и обратно. Кроме того, в WindowsNT/2k старший бит этого поля служит для индикации того, что объект ядра, хендл которого находится в поле LockSemaphore, должен быть создан заранее. Если системных ресурсов для этого недостаточно, система сгенерирует исключение, и программа может "урезать" свою функциональнось. Или совсем завершить работу.

API для работы с критическими секциями

BOOL InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount );

Заполняют поля структуры, адресуемой lpCriticalSection.

После вызова любой из этих функций критическая секция готова к работе.

Листинг 1. Псевдокод RtlInitializeCriticalSection из ntdll.dll

VOID RtlInitializeCriticalSection(LPRTL_CRITICAL_SECTION pcs) {

RtlInitializeCriticalSectionAndSpinCount(pcs, 0);

}

VOID RtlInitializeCriticalSectionAndSpinCount(LPRTL_CRITICAL_SECTION pcs, DWORD dwSpinCount) {

pcs->DebugInfo = NULL;

pcs->LockCount = -1;

pcs->RecursionCount = 0;

pcs->OwningThread = 0;

pcs->LockSemaphore = NULL;

pcs->SpinCount = dwSpinCount;

if (0x80000000 & dwSpinCount) _CriticalSectionGetEvent(pcs);

}

DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection , DWORD dwSpinCount);

Устанавливает значение поля SpinCount и возвращает его предыдущее значение. Напоминаю, что старший бит отвечает за "привязку" события, используемого для ожидания доступа к данной критической секции.

Листинг 2. Псевдокод RtlSetCriticalSectionSpinCount из ntdll.dll

DWORD RtlSetCriticalSectionSpinCount(LPRTL_CRITICAL_SECTION pcs, DWORD dwSpinCount) {

DWORD dwRet = pcs->SpinCount;

pcs->SpinCount = dwSpinCount;

return dwRet;

}

VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

Освобождает ресурсы, занимаемые критической секцией.

Листинг 3. Псевдокод RtlDeleteCriticalSection из ntdll.dll

VOID RtlDeleteCriticalSection(LPRTL_CRITICAL_SECTION pcs) {

pcs->DebugInfo = NULL;

pcs->LockCount = -1;

pcs->RecursionCount = 0;

pcs->OwningThread = 0;

if (pcs->LockSemaphore) {

::CloseHandle(pcs->LockSemaphore);

pcs->LockSemaphore = NULL;

}

}

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

Осуществляют "захват" критической секции. Если критическая секция занята другой нитью, то ::EnterCriticalSection() будет ждать, пока та освободится, а ::TryEnterCriticalSection() вернет FALSE.

Листинг 4. Псевдокод RtlEnterCriticalSection из ntdll.dll

VOID RtlEnterCriticalSection(LPRTL_CRITICAL_SECTION pcs) {

if (::InterlockedIncrement(&pcs->LockCount)) {

if (pcs->OwningThread == (HANDLE)::GetCurrentThreadId()) {

pcs->RecursionCount++;

return;

}

RtlpWaitForCriticalSection(pcs);

}

pcs->OwningThread = (HANDLE)::GetCurrentThreadId();

pcs->RecursionCount = 1;

}

BOOL RtlTryEnterCriticalSection(LPRTL_CRITICAL_SECTION pcs) {

if (-1L == ::InterlockedCompareExchange(&pcs->LockCount, 0, -1)) {

pcs->OwningThread = (HANDLE)::GetCurrentThreadId();

pcs->RecursionCount = 1;

} else if (pcs->OwningThread == (HANDLE)::GetCurrentThreadId()) {

::InterlockedIncrement(&pcs->LockCount);

pcs->RecursionCount++;

} else return FALSE;

return TRUE;

}

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection );

Освобождает критическую секцию

Листинг 5. Псевдокод RtlLeaveCriticalSection из ntdll.dll

VOID RtlLeaveCriticalSectionDbg(LPRTL_CRITICAL_SECTION pcs) {

if (--pcs->RecursionCount) ::InterlockedDecrement(&pcs->LockCount);

else if (::InterlockedDecrement(&pcs->LockCount) >= 0) RtlpUnWaitCriticalSection(pcs);

}

Классы-обертки для критических секций

Листинг 6. Код классов CLock, CAutoLock и CScopeLock

class CLock {

friend class CScopeLock;

CRITICAL_SECTION m_CS;

public:

void Init() { ::InitializeCriticalSection(&m_CS); }

void Term() { ::DeleteCriticalSection(&m_CS); }

void Lock() { ::EnterCriticalSection(&m_CS); }

BOOL TryLock() { return ::TryEnterCriticalSection(&m_CS); }

void Unlock() { ::LeaveCriticalSection(&m_CS); }

};

class CAutoLock : public CLock {

public:

CAutoLock() { Init(); }

~CAutoLock() { Term(); }

};

class CScopeLock {

LPCRITICAL_SECTION m_pCS;

public:

CScopeLock(LPCRITICAL_SECTION pCS) : m_pCS(pCS) { Lock(); }

CScopeLock(CLock& lock) : m_pCS(&lock.m_CS) { Lock(); }

~CScopeLock() { Unlock(); }

void Lock() { ::EnterCriticalSection(m_pCS); }

void Unlock() { ::LeaveCriticalSection(m_pCS); }

};

Классы CLock и CAutoLock удобно использовать для синхронизации доступа к переменным класса, а CScopeLock предназначен, в основном, для использования в процедурах. Удобно, что компилятор сам позаботится о вызове ::LeaveCriticalSection() через наш деструктор.

Листинг 7. Пример использования CScopeLock

CAutoLock m_lockObject;

CObject *m_pObject;

void Proc1() {

CScopeLock lock(m_lockObject); // Вызов lock.Lock();

if (!m_pObject) return; // Вызов lock.Unlock();

m_pObject->SomeMethod();

// Вызов lock.Unlock();

}

Отладка критических секций

Весьма интересное и увлекательное занятие. Можно потратить часы и недели, но так и не найти, где именно возникает проблема. Стоит уделить этому особо пристальное внимание. Ошибки, связанные с критическими секциями бывают двух типов: ошибки реализации и архитектурные ошибки.

Ошибки, связанные с реализацией

Это довольно легко обнаруживаемые ошибки, как правило, связанные с непарностью вызовов ::EnterCriticalSection() и ::LeaveCriticalSection().

Листинг 8. Пропущен вызов ::EnterCriticalSection()

// Процедура предполагает, что m_lockObject.Lock(); уже был вызван

void Pool() {

for (int i = 0; i < m_vectSinks.size(); i++) {

m_lockObject.Unlock();

m_vectSinks[i]->DoSomething();

m_lockObject.Lock();

}

}

::LeaveCriticalSection() без ::EnterCriticalSection() приведет к тому, что первый же вызов ::EnterCriticalSection() остановит выполнение нити навсегда.

Листинг 9. Пропущен вызов ::LeaveCriticalSection()

void Proc() {

m_lockObject.Lock();

if (!m_pObject) return;

// ...

m_lockObject.Unlock();

}

В этом примере, конечно, имеет смысл воспользоваться классом типа CScopeLock.

Кроме того, случается, что ::EnterCriticalSection() вызывается без инициализации критической секции с помощью ::InitializeCriticalSection(). Особенно часто такое случается с проектами, написанными с помощью ATL. Причем в debug-версии все работает замечательно, а release-версия рушится. Это происходит из-за так называемой "минимальной" CRT (_ATL_MIN_CRT), которая не вызываетконструкторы статических объектов ( Q166480, Q165076). В ATL версии 7.0 эту проблему решили.

Еще я встречал такую ошибку: программист пользовался классом типа CScopeLock, но для экономии места называл эту переменную одной буквой:

Читать дальше
Тёмная тема
Сбросить

Интервал:

Закладка:

Сделать


Алекс Jenter читать все книги автора по порядку

Алекс Jenter - все книги автора в одном месте читать по порядку полные версии на сайте онлайн библиотеки LibKing.




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


Отзывы читателей о книге Программирование на Visual C++. Архив рассылки, автор: Алекс Jenter. Читайте комментарии и мнения людей о произведении.


Понравилась книга? Поделитесь впечатлениями - оставьте Ваш отзыв или расскажите друзьям

Напишите свой комментарий
x