» » » Алекс 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, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.






} 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() в этой нити никогда не вернется. Он будет вечно ждать неизвестно чего.

Это только цветочки. С ягодками Вы очень быстро столкнетесь, если попытаетесь написать что-нибудь действительно сложное. Например, ActiveX-объект в многопоточном подразделении (MTA), создаваемый из скрипта, запущенного из-под контейнера, размещенного в однопоточном подразделении (STA). Ни слова не понятно? Не беда. Сейчас я попытаюсь выразить проблему более понятным языком. Итак. Имеется объект, вызывающий методы другого объекта, причем живут они в разных нитях. Вызовы производятся синхронно. Т.е. объект #1 переключает выполнение на нить объекта #2, вызывает метод и переключается обратно на свою нить. При этом выполнение нити #1 приостановлено до тех пор, пока не отработает нить объекта #2. Теперь положим, объект #2 вызывает метод объекта #1 из своей нити. Получается, что управление вернулось в объект #1, но из нити объекта #2. Если объект #1 вызывал метод объекта #2, захватив какую-либо критическую секцию, то при вызове метода объекта #1 тот заблокирует сам себя при повторном входе в ту же критическую секцию.

Листинг 11. Самоблокировка средствами одного объекта

// Нить #1

void IObject1::Proc1() {

 // Входим в критическую секцию объекта #1

 m_lockObject.Lock();

 // Вызываем метод объекта #2, происходит переключение на нить объекта #2

 m_pObject2->SomeMethod();

 // Сюда мы попадем только по возвращении из

 m_pObject2->SomeMethod();

 m_lockObject.Unlock();

}


// Нить #2

void IObject2::SomeMethod() {

 // Вызываем метод объекта #1 из нити объекта #2

 m_pObject1->Proc2();

}


// Нить #2

void IObject1::Proc2() {

 // Пытаемся войти в критическую секцию объекта #1

 m_lockObject.Lock();

 // Сюда мы не попадем никогда

 m_lockObject.Unlock();

}

Если бы в примере не было переключения нитей, все вызовы произошли бы в нити объекта #1, и никаких проблем не возникло. Сильно надуманный пример? Ничуть. Именно переключение ниток лежит в основе подразделений COM (apartments). А из этого следует одно очень, очень неприятное правило.

СОВЕТ

Избегайте вызовов каких бы то ни было объектов при захваченных критических секциях.

Помните пример из начала статьи? Так вот, он абсолютно неприемлем в подобных случаях. Его придется переделать на что-то вроде

Листинг 12. Простой пример, не подверженный самоблокировке

// Нить #1

void Proc1() {

 m_lockObject.Lock();

 CComPtr<IObject> pObject(m_pObject); // вызов pObject->AddRef();

 m_lockObject.Unlock();

 if (pObject) pObject->SomeMethod();

}


// Нить #2

void Proc2(IObject *pNewObject) {

 m_lockObject.Lock();

 m_pObject = pNewobject;

 m_lockObject.Unlock();

}

Доступ к объекту остался по-прежнему синхронизован, но вызов SomeMethod(); происходит вне критической секции. Победа? Почти. осталась одна маленькая деталь. Давайте посмотрим, что происходит в Proc2():

void Proc2(IObject *pNewObject) {

 m_lockObject.Lock();

 if (m_pObject.p) m_pObject.p->Release();

 m_pObject.p = pNewobject;

 if (m_pObject.p) m_pObject.p->AddRef();

 m_lockObject.Unlock();

}

Очевидно, что вызовы m_pObject.p->AddRef(); и m_pObject.p->Release(); происходят внутри критической секции. И если вызов метода AddRef(), как правило, безвреден, то вызов метода Release() может оказаться последним вызовом Release(), и объект самоуничтожится. В методе FinalRelease() объекта #2 может быть все что угодно, например, освобождение объектов, живущих в других подразделениях. А это опять приведет к переключению ниток и может вызвать самоблокировку объекта #1 по уже известному сценарию. Придется воспользоваться той же техникой, что и в методе Proc1().

// Нить #2 void Proc2(IObject *pNewObject) {

 CComPtr<IObject> pPrevObject;

 m_lockObject.Lock();

 pPrevObject.Attach(m_pObject.Detach());

 m_pObject = pNewobject;

 m_lockObject.Unlock(); // pPrevObject.Release();

}

Теперь потенциально последний вызов IObject2::Release() будет осуществлен после выхода из критической секции. А присвоение нового значения по-прежнему синхронизовано с вызовом IObject2::SomeMethod() из нити #1.

Способы обнаружения ошибок

Сначала стоит обратить внимание на "официальный" способ обнаружения блокировок. Если бы кроме ::EnterCriticalSection() и ::TryEnterCtiticalSection() существовал бы еще и ::EnterCriticalSectionWithTimeout(), то достаточно было бы просто указать какое-нибудь резонное значение для интервала ожидания, например, 30 секунд. Если критическая секция не освободилась в течение указанного времени, то с очень большой вероятностью она не освободится никогда. Имеет смысл подключить отладчик и посмотреть, что же творится в соседних нитьх. Но увы. Никаких ::EnterCriticalSectionWithTimeout() в Win32 не предусмотрено. Вместо этого есть поле CriticalSectionDefaultTimeout в структуре IMAGE_LOAD_CONFIG_DIRECTORY32, которое всегда равно нулю и, судя по всему, не используется. Зато используется ключ в реестре "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\CriticalSectionTimeout", который по умолчанию равен 30 суткам, и по истечению этого времени в системный лог попадает строка "RTL: Enter Critical Section Timeout (2 minutes)\nRTL: Pid.Tid XXXX.YYYY, owner tid ZZZZ\nRTL: Re-Waiting\n". К тому же это верно только для систем WindowsNT/2k/XP и только с CheckedBuild. У вас установлен CheckedBuild? Нет? А зря. Вы теряете исключительную возможность увидеть эту замечательную строку.

Ну, а какие у нас альтернативы? Да, пожалуй, только одна. Не использовать API для работы с критическими секциями. Вместо них написать свои собственные. Пусть даже не такие обточенные напильником, как в WindowsNT. Не страшно. Нам это понадобится только в debug-конфигурациях. В release'ах мы будем продолжать использовать оригинальный API от Майкрософт. Для этого напишем несколько функций полностью совместимых по типам и количеству аргументов с "настоящим" API и добавим #define как у MFC для переопределения оператора new в debug-конфигурациях.

Листинг 14. Собственная реализация критических секций

#if defined(_DEBUG) && !defined(_NO_DEADLOCK_TRACE)

#define DEADLOCK_TIMEOUT 30000

#define CS_DEBUG 1


// Создаем на лету событие для операций ожидания,

// но никогда его не освобождаем. Так удобней для отладки

static inline HANDLE _CriticalSectionGetEvent(LPCRITICAL_SECTION pcs) {

 HANDLE ret = pcs->LockSemaphore;

 if (!ret) {

  HANDLE sem = ::CreateEvent(NULL, false, false, NULL);

  ATLASSERT(sem);

  if (!(ret = (HANDLE)::InterlockedCompareExchangePointer(

   &pcs->LockSemaphore, sem, NULL))) ret = sem;

  else ::CloseHandle(sem); // Кто-то успел раньше

 }

 return ret;

}


// Ждем, пока критическая секция не освободится либо время ожидания

// будет превышено

static inline VOID _WaitForCriticalSectionDbg(LPCRITICAL_SECTION pcs) {

 HANDLE sem = _CriticalSectionGetEvent(pcs);

 DWORD dwWait;

 do {

  dwWait = ::WaitForSingleObject(sem, DEADLOCK_TIMEOUT);

  if (WAIT_TIMEOUT == dwWait) {

   ATLTRACE("Critical section timeout (%u msec):"

    " tid 0x%04X owner tid 0x%04X\n", DEADLOCK_TIMEOUT,

    ::GetCurrentThreadId(), pcs->OwningThread);

  }

} while(WAIT_TIMEOUT == dwWait);

 ATLASSERT(WAIT_OBJECT_0 == dwWait);

}


// Выставляем событие в активное состояние

static inline VOID _UnWaitCriticalSectionDbg(LPCRITICAL_SECTION pcs) {

 HANDLE sem = _CriticalSectionGetEvent(pcs);

 BOOL b = ::SetEvent(sem);

 ATLASSERT(b);

}


// Заполучем критическую секцию в свое пользование

inline VOID EnterCriticalSectionDbg(LPCRITICAL_SECTION pcs) {

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

  // LockCount стал больше нуля.

  // Проверяем идентификатор нити

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

   // Нить та же самая. Критическая секция наша.

   pcs->RecursionCount++;

   return;

  }

  // Критическая секция занята другой нитью.

  // Придется подождать

  _WaitForCriticalSectionDbg(pcs);


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

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

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


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

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

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

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

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

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

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