
- •1. Синхронизирующие объекты
- •1.1. Критические разделы
- •Синхронизация потоков с объектами ядра
- •2.1. Объекты mutex
- •2.2.Семафоры
- •2.3.События
- •2.4.События со сбросом вручную
- •2.5.События с автоматическим сбросом
- •InitializeCriticalSection(&g_CriticalSection);
- •0, &DwThreadId);
- •0, &DwThreadId);
- •Void Dlg_OnDestroy (hwnd hwnd) {
- •Void Dlg_OnCommand (hwnd hwnd, int id, hwnd hwndCtl,
- •If (Button_GetCheck(hwndCtl)) {
- •Int winapi WinMain (hinstance hinstExe,
2.3.События
События - самая примитивная разновидность синхронизирующих объектов, резко отличающаяся от семафоров и объектов mutex. Последние обычно применяются для контроля за доступом к данным, а события просто оповещают об окончании какой-либо операции. Существуют два разных типа объектов "событие": со сбросом вручную (manual-reset events) и с автоматическим сбросом (auto-reset events). Первые используются для оповещения об окончании операции срезу нескольких потоков, вторые для оповещения единственного потока.
К событиям обычно прибегают в том случае, когда один поток выполняет какую-либо инициализацию, а затем сигнализирует другому потоку, что тот может работать дальше. Инициализирующий поток переводит объект "событие" в занятое (non-signaled) состояние и приступает к своим операциям. По окончании инициализации поток возвращает событие в свободное (singnaled) состояние. В то же время рабочий поток приостанавливает свое исполнение и ждет перехода событий в свободное состояние. Как только инициализирующий поток просигнализирует событие (т.е. освободит его), рабочий поток "проснется" и продолжит работу.
Например, в процессе исполняются два потока. Первый считывает данные из файла в буферную память и оповещает второй поток, что можно заняться обработкой данных. Закончив обработку, второй поток сигнализирует первому, чтобы тот загрузил новый блок данных из файла и т.д.
Семантика функций, оперирующих с событиями, идентична семантике тех же функций, предназначенных для объектов mutex и семафоров. Событие создается функцией GreatEvent:
HANDLE CreateEvent (LPSECURITY_ATTRIBUTES lpsa
BOOL fManualReset, BOOL fInitialState, LPTSTR lpszEventName);
Параметр fManualReset - булева переменная - сообщает системе создается ли событие со сбросом вручную (TRUE) или с автоматическим сбросом (FALSE). Параметр fInitialState указывает начальное состояние события: свободное (TRUE) или занятое (FALSE). После того как система создает объект "событие", CreateEvent возвращает "процессо-зависимый" описатель события. Потоки из других процессов могут получить к нему доступ:
вызовом CreateEvent с тем же параметром lpszEventName;
наследованием;
применением DuplicateHandle;
вызовом OpenEvent с указанием в ее параметре lpszEventName имени объекта "событие", совпадающего с тем, что указано в аналогичном параметре функции CreateEvent. Функция OpenEvent:
HANDLE OpenEvent (DWORD fdwAccess, BOOL fInherit,
LPTSTR lpszEventName);
Закрытие событий осуществляется функцией CloseHandle.
2.4.События со сбросом вручную
События этого типа автоматически не переустанавливаются в занятое состояние функциями WaitForSingleObject и WaitForMultipleObjects. В случае объектов mutex, когда поток вызывает WaitForSingleObject или WaitForMultipleObjects, функция автоматически переводит mutex в занятое состояние и ждет его освобождения. Это гарантирует, что только один поток, ожидающий объект mutex, получит его в свое распоряжение и сможет продолжить исполнение кода. Если бы сами потоки отвечали за возврат mutex в занятое состояние, то объект могли бы захватить два и более потоков - прежде чем один из них успел бы сбросить его состояние.
Иначе обстоит дело в отношении событий со сбросом вручную. Может быть несколько потоков, ждущих возникновения одного события. Когда оно происходит, каждый из ожидавших потоков получает возможность выполнить свои операции. Допустим, один поток отвечает за считывание данных из файла в буфер. После того, как данные считаны, запускаются еще девять других потоков. Каждый из них обрабатывает данные по-своему. Предположим, в файле находится документ, созданный каким-нибудь текстовым процессором. Тогда пусть первый поток подсчитывает символы, второй - слова, третий - страницы, четвертый проверяет орфографию, пятый печатает документ и т.п. Общим у всех потоков является то, что ни один из них ничего не записывает в файл, т. е. данные для них - ресурс, открытый только для чтения.
Очевидно, что по возникновении события все ожидающие потоки должны возобновить исполнение. Этот пример и является одним из случаев применения событий со сбросом вручную. Когда объект "событие со сбросом вручную" переходит в свободное состояние, система разрешает исполнение всех потоков, его ожидавших. Поток переводит объект "событие" в свободное состояние вызовом функции SetEvent:
BOOL SetEven (HANDLE hEvent);
Функция принимает описатель объекта "событие" и меняет состояние объекта на свободное. Если операция выполнена успешно, функция возвращает TRUE. После перевода объекта "событие" в свободное состояние он пребывает в нем, пока какой-нибудь поток явным образом (т.е. "вручную") не сбросит событие:
BOOL ResetEvent (HANDLE hEvent);
Эта функция принимает описатель объекта "событие" и переводит объект в занятое состояние. Если операция прошла успешно функция возвращает TRUE.
В рассмотренном примере поток, считывающий данные из файла в общий буфер памяти, должен вызвать ResetEvent как раз перед этой операцией, и, закончив загрузку данных, - обратиться к SetEvent.
После окончания работы прочих потоков с текущей порцией данных потоку, считывающему данные из файла, необходимо об этом сигнализировать. Лучше всего создать в каждом из потоков собственный объект "событие" и описатели этих объектов поместить в массив; тогда поток, отвечающий за чтение данных, сможет воспользоваться функцией WaitForMultipleObjects и сообщить, что собирается ждать освобождения всех девяти объектов.
Поскольку нередко после вызова SetEven объект "событие" освобождается и тут же следует вызов ResetEvent, то в Win32 предусмотрена дополнительная функция, способная самостоятельно выполнять все три операции:
BOOL PulseEvent (HANDLE hEvent);
После возврата управления функцией PulseEvent событие остается в занятом состоянии. Если функция выполнена успешно, возвращается TRUE.