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

на главную - закладки

Жанры

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

Jenter Алекс

Шрифт:

 // ...

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

Это только цветочки. С ягодками Вы очень быстро столкнетесь, если попытаетесь написать что-нибудь действительно сложное. Например, 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.

Поделиться:
Популярные книги

Моя на одну ночь

Тоцка Тала
Любовные романы:
современные любовные романы
короткие любовные романы
5.50
рейтинг книги
Моя на одну ночь

Черный Маг Императора 8

Герда Александр
8. Черный маг императора
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Черный Маг Императора 8

Измена. Отбор для предателя

Лаврова Алиса
1. Отбор для предателя
Фантастика:
фэнтези
5.00
рейтинг книги
Измена. Отбор для предателя

Кодекс Крови. Книга II

Борзых М.
2. РОС: Кодекс Крови
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Кодекс Крови. Книга II

Шаг в бездну

Муравьёв Константин Николаевич
3. Перешагнуть пропасть
Фантастика:
фэнтези
космическая фантастика
7.89
рейтинг книги
Шаг в бездну

Часовая битва

Щерба Наталья Васильевна
6. Часодеи
Детские:
детская фантастика
9.38
рейтинг книги
Часовая битва

Вечная Война. Книга II

Винокуров Юрий
2. Вечная война.
Фантастика:
юмористическая фантастика
космическая фантастика
8.37
рейтинг книги
Вечная Война. Книга II

Хроники странного королевства. Вторжение. (Дилогия)

Панкеева Оксана Петровна
110. В одном томе
Фантастика:
фэнтези
9.38
рейтинг книги
Хроники странного королевства. Вторжение. (Дилогия)

Часовой ключ

Щерба Наталья Васильевна
1. Часодеи
Фантастика:
фэнтези
9.36
рейтинг книги
Часовой ключ

Инвестиго, из медика в маги

Рэд Илья
1. Инвестиго
Фантастика:
фэнтези
городское фэнтези
попаданцы
5.00
рейтинг книги
Инвестиго, из медика в маги

Кротовский, может, хватит?

Парсиев Дмитрий
3. РОС: Изнанка Империи
Фантастика:
попаданцы
альтернативная история
аниме
7.50
рейтинг книги
Кротовский, может, хватит?

Драконий подарок

Суббота Светлана
1. Королевская академия Драко
Любовные романы:
любовно-фантастические романы
7.30
рейтинг книги
Драконий подарок

Очешуеть! Я - жена дракона?!

Амеличева Елена
Фантастика:
юмористическая фантастика
5.43
рейтинг книги
Очешуеть! Я - жена дракона?!

Идеальный мир для Лекаря 9

Сапфир Олег
9. Лекарь
Фантастика:
боевая фантастика
юмористическое фэнтези
6.00
рейтинг книги
Идеальный мир для Лекаря 9