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


Авторские права

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

Здесь можно скачать бесплатно "Алекс Jenter - Программирование на Visual C++. Архив рассылки" в формате fb2, epub, txt, doc, pdf. Жанр: Программирование. Так же Вы можете читать книгу онлайн без регистрации и SMS на сайте LibFox.Ru (ЛибФокс) или прочесть описание и ознакомиться с отзывами.
Рейтинг:
Название:
Программирование на Visual C++. Архив рассылки
Автор:
Издательство:
неизвестно
Год:
неизвестен
ISBN:
нет данных
Скачать:

99Пожалуйста дождитесь своей очереди, идёт подготовка вашей ссылки для скачивания...

Скачивание начинается... Если скачивание не началось автоматически, пожалуйста нажмите на эту ссылку.

Вы автор?
Жалоба
Все книги на сайте размещаются его пользователями. Приносим свои глубочайшие извинения, если Ваша книга была опубликована без Вашего на то согласия.
Напишите нам, и мы в срочном порядке примем меры.

Как получить книгу?
Оплатили, но не знаете что делать дальше? Инструкция.

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

Описание и краткое содержание "Программирование на Visual C++. Архив рассылки" читать бесплатно онлайн.



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






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, но для экономии места называл эту переменную одной буквой:

CScopeLock l(m_lock);

и как-то раз просто пропустил имя у переменной. Получилось

CScopeLock (m_lock);

а что это означает? Компилятор честно сделал вызов конструктора CScopeLock, и тут же уничтожил этот безымянный объект, как и положено по стандарту. Т.е. сразу же после вызова метода Lock() последовал вызов Unlock(), и синхронизация перестала иметь место. Вообще, давать переменным, даже локальным, имена из одной буквы – путь быстрого наступления на всяческие грабли.

СОВЕТ

Если у Вас в процедуре больше одного цикла, то вместо int i, j, k стоит все-таки использовать что-то вроде int nObject, nSection, nRow.

Архитектурные ошибки

Самая известная из них это блокировка (deadlock) когда две нити пытаются захватить две или более критических секций, причем делают это в разном порядке.

Листинг 10. Взаимоблокировка двух ниток

void Proc1() // Нить #1

{

 ::EnterCriticalSection(&m_lock1);

 // ...

 ::EnterCriticalSection(&m_lock2);

 // ...

 ::LeaveCriticalSection(&m_lock2);

 // ...

 ::LeaveCriticalSection(&m_lock1);

}


// Нить #2

void Proc2() {

 ::EnterCriticalSection(&m_lock2);

 // ...   

 ::EnterCriticalSection(&m_lock1);

 // ...   

 ::LeaveCriticalSection(&m_lock1);

 // ...   

 ::LeaveCriticalSection(&m_lock2);

}

Еще могут возникнуть проблемы при… копировании критических секций. Понятно, что вот такой код вряд ли сможет написать программист в здравом уме и памяти:

CRITICAL_SECTION sec1;

CRITICAL_SECTION sec2;

// …

sec1 = sec2;

Из такого присвоения трудно извлечь какую-либо пользу. А вот такой код иногда пишут:

struct SData {

 CLock _lock;

 DWORD m_dwSmth;

} m_data;


void Proc1(SData& data) {

 m_data = data;

}

и все бы хорошо, если бы у структуры SData был конструктор копирования, например такой:

SData(const SData data) {

 CScopeLock lock(data.m_lock);

 m_dwSmth = data.m_dwSmth;

}

но нет, программист посчитал, что хватит за глаза простого копирования полей и, в результате, переменная m_lock была просто скопирована, хотя именно в этот момент из другой нити она была "захвачена" и значение поля LockCount у нее в этот момент больше либо равен нулю. После вызова ::LeaveCriticalSection() в той нити, у исходной переменной m_lock значение поля LockCount уменьшилось на единицу. А у скопированно переменной – осталось прежним. И любой вызов ::EnterCriticalSection() в этой нити никогда не вернется. Он будет вечно ждать неизвестно чего.


На Facebook В Твиттере В Instagram В Одноклассниках Мы Вконтакте
Подписывайтесь на наши страницы в социальных сетях.
Будьте в курсе последних книжных новинок, комментируйте, обсуждайте. Мы ждём Вас!

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

Книги похожие на "Программирование на Visual C++. Архив рассылки" читать онлайн или скачать бесплатно полные версии.


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

Все книги автора Алекс Jenter

Алекс Jenter - все книги автора в одном месте на сайте онлайн библиотеки LibFox.

Уважаемый посетитель, Вы зашли на сайт как незарегистрированный пользователь.
Мы рекомендуем Вам зарегистрироваться либо войти на сайт под своим именем.

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

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

А что Вы думаете о книге? Оставьте Ваш отзыв.