Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лабораторный практикум «Основы разработки приложений Windows» книга 2.DOC
Скачиваний:
91
Добавлен:
10.05.2014
Размер:
827.9 Кб
Скачать

Синхронизация с помощью состояний потока

Представим себе электронную таблицу, в которой хранятся взаимосвязанные данные, т. е. исходные данные вместе с результатами их обработки. Ввод новых данных в таблицу или изменение старых потребует пересчета всей или части таблицы. Если таблица велика, а формулы, связывающие данные, сложны, этот пересчет может занять весьма значительное время, и в течение этого времени программа будет “висеть”, не позволяя пользователю вводить новые данные. Для повышения эффективности программы можно алгоритм пересчета данных выделить в отдельный поток, который будет выполняться параллельно с основным. Тогда при вводе данных, например, с клавиатуры, когда процессор практически свободен, процессорное время будет отдаваться потоку пересчета, не замедляя ввод новых данных. Если еще воспользоваться встроенной в Windows системой приоритетов и назначить потоку пересчета низкий приоритет, то эффективность программы еще более повысится, так как поток пересчета не будет претендовать на время процессора, пока выполняются какие-либо более приоритетные потоки. Однако возникновение любой паузы в выполнении приоритетных потоков приведет к активизации потока пересчета.

Однако в описываемой задаче может возникнуть ситуация, когда первичный поток, дойдя до некоторой точки, не может продолжить выполнение, пока не будут пересчитаны вседанные. Если считать, что вторичный поток завершается, закончив пересчет данных, а при вводе новых данных запускается снова, то в рассматриваемом случае первичный поток должен остановиться в “критической точке” (когда ему понадобились обновленные данные) и ждать завершения вторичного. Взаимодействие таких потоков вместе с функциями, которые его организуют, показано на рис. 7.2.

Рис. 7.2. Синхронизация с помощью объекта “поток”

В некотором месте приложение (строго говоря, его первичный поток) создает вторичный поток с рабочей функцией Calc(), и начиная с этого момента в системе параллельно выполняются оба потока, и первичный, и вторичный. В той точке, где приложению требуются результаты выполнения потокаCalc(), вызывается функция ожидания одного объектаWaitForSingleObject(), которая останавливает дальнейшее выполнение первичного потока в ожидании установки состояния вторичного. Однако пока вторичный поток выполняется, его состояние сброшено, и первичный поток, “застряв” на функцииWaitForSingleObject(), будет находиться в спящем состоянии. Завершение рабочей функцииCalc()приводит к завершению вторичного потока, установке его состояния и снятию блокировки первичного потока, который может теперь воспользоваться результатами вычислений, выполненных во вторичном потоке.

Синхронизация с помощью событий

Объект “событие” относится к числу весьма эффективных средств синхронизации потоков и процессов. С помощью этого объекта поток может уведомить другой поток об окончании любой операции или вообще о возникновении любой оговоренной заранее ситуации, могущей повлиять на выполнение другого потока. Другими словами, события не привязаны к определенным действиям, например, завершению процесса, окончанию операции ввода-вывода и др. Программист имеет возможность устанавливать и сбрасывать события в любой точке программы, организуя тем самым взаимосвязь потоков нужным ему образом. Заметим, что, поскольку при создании события ему можно дать произвольное имя и затем открыть это событие в другом процессе, события можно использовать для синхронизации не только потоков одного процесса, но и самостоятельных процессов, точнее, потоков, работающих в контексте самостоятельных процессов.

Перед тем, как использовать событие в целях синхронизации, его надо создать. Событие создается с помощью функции Cre­ateEvent()со следующим прототипом:

HANDLE CreateEvent(

LPSECURITY_ATTRIBUTES

lpEventAttributes,//Адрес атрибутов защиты

BOOL bManualReset,//Флаг ручного сброса события

BOOL bInitialState,//Флаг начального состояния события

LPCTSTR lpName//Имя события

);

Существует два типа событий: со сбросом вручную и с автоматическим сбросом (автосбросом). События со сбросом вручную требуют явной установки их в свободное состояние функцией SetEvent()и столь же явного переключения в занятое состояние функциейResetEvent(); очевидно, что эти переключения можно выполнять в произвольных точках программы. События с автосбросом точно так же устанавливаются явным образом функциейSetEvent(), однако их сброс происходит автоматически, как только в управляемом потоке функция ожидания события обнаружила, что событие установлено и, соответственно, разбудила поток. Событиями с автосбросом удобно пользоваться в циклических фрагментах программ, так как такое событие, автоматически сброшенное функциейWaitForSingleObject()в начале разблокированного ею же фрагмента, предотвращает повторное его выполнение до новой установки этого события в управляющем потоке.

Если в функции CreateEvent()параметрbManualResetравенTRUE, создается событие со сбросом вручную; если этот параметр равенFALSE, создается событие с автосбросом.

Параметр bInitialStateзадает начальное состояние создаваемого события. Если он равенTRUE, создается установленное событие; если этот параметр равенFALSE, созданное событие будет сброшено.

Функция CreateEvent(), как и другие функции создания объектов Windows, возвращает дескриптор созданного события типаHANDLE, который затем используется в функциях переустановки и ожидания события.

Для установки события в свободное (сигнальное) состояние используется функция SetEvent(), в качестве параметра которой указывается дескриптор устанавливаемого события. Если событие удалось установить, функция возвращает значениеTRUE.

Для сброса события в занятое (несигнальное) состояние используется функция ResetEvent(), в качестве параметра которой указывается дескриптор сбрасываемого события. Если событие удалось сбросить, функция возвращает значениеTRUE.

На рис. 7.3 показан пример взаимодействия потоков, синхронизация которых осуществляется с помощью двух событий.

Первичный поток создает два события в сброшенном состоянии, сохраняя их дескрипторы hEvent1иhEvent2, после чего запускает вторичный поток, из которого должен получить последовательно два результата. Созданные события пока никак не влияют на ход приложения. Оба потока выполняются параллельно до тех пор, пока первичному потоку не потребуется первый результат из вторичного. В этой точке первичный поток останавливается вызовом функцииWaitForSingleObject(), в качестве первого параметра которой указывается дескриптор первого (пока сброшенного) событияhEvent1. Как только вторичный поток получил первый результат, он вызовом функцииSetEvent(hEvent1)устанавливает первое событие, что приводит к “побудке” первичного потока. Такая же процедура (только с использованием второго события) повторяется, когда первичный поток доходит до точки, в которой ему нужен второй результат из вторичного потока.

Рис. 7.3. Синхронизация потоков с помощью событий