Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Обучение VC++ / ЛекцииИнтернетС++ / Лекция_лаб_практикум.doc
Скачиваний:
64
Добавлен:
16.02.2016
Размер:
932.35 Кб
Скачать

Блокировка потоков

Пример блокировки потока — первый вызов WaitForSingleObject в функции ComputeThreadProc. Поток просто прекращает выполнение до тех пор, пока событие не перейдет в свободное состояние. Есть и много других способов заблокировать поток. Можно, например, вызвать Win32-функцию Sleep, чтобы “усыпить” поток на 500 миллисекунд. Блокировку потока вызывают и функции, которые обращаются к устройствам вроде коммуникационных портов или дисков. Во времена Win 16 эти функции захватывали процессор до тех пор, пока их работа не заканчивалась, а в Win32 они позволяют выполняться другим процессам и потокам.

Следует избегать блокирующих вызовов в основном потоке пользовательского интерфейса. Если заблокировать основной поток, он не сможет обрабатывать сообщения, а из-за этого программа окажется весьма медленной. Если у Вас есть задача, требующая интенсивных операций дискового ввода/вывода, поместите соответствующий код в рабочий поток и синхронизируйте его со своим основным потоком.

Будьте осторожны в рабочем потоке с вызовами, которые могут заблокировать его на неограниченное время. Если же вызов блокирует поток навечно, то поток все равно завершится при завершении основного потока процесса, но тогда не исключены утечки памяти. Можно также вызвать из основного потока функцию Win32 TerminateThread, но проблема утечек памяти остается и в этом случае.

Критические секции

Помните проблемы с доступом к глобальной переменной g_nCount? Если необходимо, чтобы несколько потоков совместно использовали глобальные данные и нуждаетесь в большей гибкости, чем та, которую предоставляют простые операторы типа Interlockedlncrement, то подойдет такое средство синхронизации, как критические секции (critical sections).

События хороши для “сигнализации”, а критические секции (секции кода, требующие монопольного доступа к разделяемым данным) удобны для управления доступом к данным.

MFC предоставляет класс CCriticalSection — “обертку” описателя критической секции Windows. Его конструктор вызывает функцию InitializeCriticalSection, функции-члены Lock и Unlock вызывают функции EnterCriticalSection и LeaveCriticalSection соответственно, а деструктор вызывает DeleteCriticalSection. Вот как можно использовать этот класс для защиты глобальных данных:

CCriticalSection g_cs;

int g_nCount;

void func()

{

g_cs.Lock();

g_nCount++;

g cs.Unlock();

}

Допустим программа отслеживает показания времени как часы, минуты и секунды, а каждое из этих значений хранится в отдельной целочисленной переменной. Теперь представим, что значения времени совместно используются двумя потоками. Поток А изменяет значение времени и прерывается потоком Б после обновления часов, но до обновления минут и секунд. Результат: поток Б получает недостоверные показания времени.

Если Вы пишете для данного формата времени класс C++, то сможете легко управлять доступом к данным, сделав элементы данных закрытыми и предусмотрев открытые функции-члены. Именно таков класс CHMS приведенный ниже. Заметьте в этом классе есть переменная-член типа CcnticalSection. Таким образом, с каждым объектом CHMS связан объект “критическая секция”.

Обратите внимание, что другие функции-члены вызывают функции-члены Lock и Unlock. Если поток A исполняется в середине SetTime, поток Б будет блокирован вызовом EnterCnticalSection в GetTotalSecs до тех пор, пока поток А не вызовет LeaveCnticalSection. Функция IncrementSecs вызывает SetTime, что означает наличие вложенных критических секций. Это допустимо, так как Windows отслеживает уровни их вложенности.

Класс CHMS отлично работает, если Вы используете его для конструирования глобальных объектов. Если же потоки вашей программы совместно используют указатели на объекты в куче, то Вы столкнетесь с рядом других проблем. Каждый поток должен определять, не удален ли объект другим потоком, а, значит, нужна синхронизация доступа к указателям.

HMS.H

include StdAfx h

class CHMS

{

private:

int m_nHr, m_nMn, m_nSc;

CCnticalSection m_cs;

public:

CHMSQ(): m_nHr(0), m_nMn(0), m_nSc(0) {}

~CHMSO {}

void SetTinie(int nSecs)

{

m_cs.Lock();

m_nSc = nSecs % 60;

m_nMn = (nSecs / 60) % 60;

m_nНr = nSecs / 3600;

m_cs.Unlock();

}

int GetTotalSecs()

{

int nTotalSecs;

m_cs.Lock();

nTotalSecs = m_nHr * 3600 + m_nMn * 60 + m_nSc;

m_cs.Unlock();

return nTotalSecs ;

}

void IncrementSecs()

{

m_cs.Lock();

SetTime (GetTotalSecs() +1);

m_cs.Unlock();

}

};