Синхронизация потоков в операционной системе Windows, страница 11

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

Вызовы CreateWaitableTimer и OpenWaitableTimer возвращают дескриптор таймера. Чтобы начать отсчет времени таймером, необходимо обратиться к функции SetWaitableTimer. Период времени указывается как значение типа LARGE_INTEGER, равное количеству единиц времени, каждая из которых равна 100 наносекундам. На самом деле используемое аппаратное обеспечение может не поддерживать измерение времени с точностью до 100 наносекунд. Если указан положительный период времени, считается, что это абсолютное время. Отрицательные значения обозначают относительное время. Можно настроить таймер таким образом, чтобы он сработал только один раз, а можно приказать ему срабатывать через равные промежутки времени.

Когда период времени истекает, таймер переходит в сигнальное состояние. Можно настроить таймер таким образом, чтобы при переходе его в сигнальное состояние автоматически выполнялся вызов функции АРС. Остановить таймер можно при помощи вызова Cance1Waitab1eTimer.

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

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

Это решит проблему, однако использование мьютекса — не самое эффективное решение в данной ситуации. Обращение к мьютексу снижает производительность программы. Это связано с тем, что мьютекс предназначен для обслуживания нескольких разных процессов. Если вы намерены обеспечить синхронную работу нескольких потоков одного и того же процесса, вы можете воспользоваться другим, более простым и эффективным решением. Для обработки подобных ситуаций Windows предлагает использовать критические секции (critical sections) (см. табл. 5). Критическая секция анализирует значение специальной переменной процесса, которая используется как флаг, предотвращающий исполнение некоторого участка кода несколькими потоками одновременно.

Чтобы защитить участок кода от совместного использования несколькими потоками, определите переменную типа CRITICAL_SECTION и передайте ее адрес функции InitializeCnticalSection. К этой функции следует обращаться из главного потока вашей программы (обычно из потока main). В начале критической секции кода следует расположить обращение к функции EnterCriticalSection. Если в этот момент критическая секция исполняется каким-либо другим потоком, исполнение потока, обратившегося к EnterCnticalSection, будет блокировано до тех пор, пока критический участок не освободится. Если вы не хотите блокировать работу потока и желаете, чтобы во время ожидания освобождения критической секции поток выполнял какую-либо другую полезную работу, вы можете использовать вызов TryEnterCriticalSection. Этот вызов возвращает значение TRUE в случае, если критический участок кода никем не занят. Если вызов вернул значение FALSE, поток может выполнить какую-либо другую работу, а затем вновь обратиться к TryEnterCriticalSection.

Вызов TryEnterCricalSecion будет продолжать возвращать FALSE, а вызов EnterCnticalSelecton будет продолжать блокировать выполнение потоков до тех пор, пока поток, выполняющий критическую секцию, не обратится к LeaveCriticalSecion. Таким образом, чаще всего функция, содержащая критический участок кода, выглядит следующим образом:

void one_at_a_time_please()

( EnterCritica1Section(&section):   // инициализация выполняется

                                                   // в другом месте