11_Синхронизация потоков в Windows.pptx
- Количество слайдов: 35
СИНХРОНИЗАЦИЯ ПОТОКОВ В WINDOWS
Критические секции Для работы с объектами типа CRITICAL_SECTION используются следующие функции: // инициализация критической секции VOID Initialize. Critical. Section(LPCRITICAL_SECTION lp. Critical. Section); // вход в критическую секцию VOID Enter. Critical. Section(LPCRITICAL_SECTION lp. Critical. Section); // попытка войти в критическую секцию BOOL Try. Enter. Critical. Section(LPCRITICAL_SECTION lp. Critical. Section); // выход из критической секции VOID Leave. Critical. Section(LPCRITICAL_SECTION lp. Critical. Section);
#include
int main() { int i, j ; HANDLE h. Thread; DWORD IDThread; h. Thread=Create. Thread(NULL, 0, thread, NULL, 0, &IDThread) ; if (h. Thread == NULL) return Get. Last. Error(); for (j = 10; j < 20; ++j) { for (i = 0; i < 10; ++i) { // выводим строку чисел j cout « j « ‘ ‘ « flush; Sleep(17); } cout « endl; } // ждем, пока поток thread закончит свою работу Wait. For. Single. Object(h. Thread, INFINITE); return 0; }
#include
int main() { int i, j; HANDLE h. Thread; DWORD IDThread; // инициализируем критическую секцию Initialize. Critical. Section(&cs); h. Thread=Create. Thread(NULL, 0, thread, NULL, 0, &IDThread); if (h. Thread == NULL) return Get. Last. Error(); for (j = 10; j < 20; ++j) { // входим в критическую секцию Enter. Critical. Section(&cs); for (i = 0; i < 10; ++i) { cout « j « ' ' « flush; Sleep(7) ; } cout « endl; // выходим из критической секции Leave. Critical. Section(&cs); } // ждем, пока поток thread закончит свою работу Wait. For. Single. Object(h. Thread, INFINITE); // закрываем критическую секцию Delete. Critical. Section(&cs); return 0; }
Объекты синхронизации и функции ожидания В операционных системах Windows объектами синхронизации называются объекты ядра, которые могут находиться в одном из двух состояний: сигнальном (signaled) и несигнальном (nonsignaled). Объекты синхронизации могут быть разбиты на четыре класса.
ü ü ü К первому классу относятся объекты синхронизации, т. е. те, которые служат только для решения задач синхронизации параллельных потоков: мьютекс (mutex); событие (event); семафор (semaphore). Ко второму классу объектов синхронизации относится ожидающий таймер (waitable timer), который переходит в сигнальное состояние по истечении заданного интервала времени. К третьему классу синхронизации относятся объекты, которые переходят в сигнальное состояние по завершении своей работы: работа (job); процесс (process); поток (thread). К четвертому классу относятся объекты синхронизации, которые переходят в сигнальное состояние после получения сообщения об изменении содержимого объекта. К ним относятся: изменение состояния каталога (change notification); консольный ввод (console input).
Функции ожидания в Windows это такие функции, параметрами которых являются объекты синхронизации. Эти функции обычно используются для блокировки потоков. Для ожидания перехода в сигнальное состояние одного объекта синхронизации используется функция Wait. For. Singie. Object, которая имеет следующий прототип: DWORD Wait. For. Single. Object( HANDLE h. Handle, // дескриптор объекта DWORD dw. Milliseconds // интервал ожидания в миллисекундах );
Для ожидания перехода в сигнальное состояние нескольких объектов синхронизации или одного из нескольких объектов синхронизации используется функция Wait. For. Muitipie. Object, которая имеет следующий прототип: DWORD Wait. For. Multiple. Objects( DWORD n. Count, // количество объектов CONST HANDLE *lp. Handles, // массив дескрипторов объектов BOOL b. Wait. All, // режим ожидания DWORD dw. Milliseconds // интервал ожидания в миллисекундах );
Мьютексы Для решения проблемы взаимного исключения между параллельными потоками, выполняющимися в контекстах разных процессов, в операционных системах Windows используется объект ядра мьютекс. Слово мьютекс проис ходит от английского слова mutex, которое в свою очередь является сокращением от выражения mutual exclusion, что на русском языке значит "взаимное исключение". Мьютекс находится в сигнальном состоянии, если он не принадлежит ни одному потоку. В противном случае мьютекс находится в несигнальном состоянии. Одновременно мьютекс может принадлежать только одному потоку.
Создается мьютекс вызовом функции Create. Mutex, которая имеет следующий прототип: HANDLE Create. Mutex( LPSECURITY_ATTRIBUTES lp. Mutex. Attributes, // атрибуты защиты BOOL blnitial. Owner, // начальный владелец мьютекса LPCTSTR lp. Name // имя мьютекса );
#include
Zero. Memory(&si, sizeof(STARTUPINFO)); si. cb = sizeof(STARTUPINFO); // создаем новый консольный процесс if (!Create. Process(Ipsz. App. Name, NULL, FALSE, NULL, &si, &pi)) { cout « "The new process is not created. " « endl; cout « "Press any key to exit. " « endl; cin. get(); return Get. Last. Error(); } // выводим на экран строки for (int j = 0; j < 10; ++j) { // захватываем мьютекс Wait. For. Single. Object(h. Mutex, INFINITE); for (int i = 0; i < 10; i++) { cout « j « ' ' « flush; Sleep(10);
} cout « endl; // освобождаем мьютекс Release. Mutex (h. Mutex) ; } // закрываем дескриптор мьютекса Close. Handle(h. Mutex); // ждем пока дочерний процесс закончит работу Wait. For. Single. Object (pi. h. Process, INFINITE) ; // закрываем дескрипторы дочернего процесса в текущем процессе Close. Handle (pi. h. Thread) ; Close. Handle(pi. h. Process); return 0; }
#include
// захватываем мьютекс Wait. For. Single. Object(h. Mutex, INFINITE); for (i = 0; i < 10; i++) { cout « j « ' ' « flush; Sleep(5); } cout « endl; // освобождаем мьютекс Release. Mutex(h. Mutex); } // закрываем дескриптор объекта Close. Handle(h. Mutex); return 0; }
События Событием называется оповещение о некотором выполненном действии. В программировании события используются для оповещения одного потока о том, что другой поток выполнил некоторое действие. Сама же задача оповещения одного потока о некотором действии, которое совершил другой поток, называется задачей условной синхронизации. В операционных системах Windows события описываются объектами ядра Events. При этом различают два типа событий: ü события с ручным сбросом; ü события с автоматическим сбросом.
Создаются события Create. Event, которая прототип: вызовом имеет HANDLE Create. Event( LPSECURITY_ATTRIBUTES Ip. Security. Attributes, // атрибуты защиты BOOL b. Manual. Reset, // тип события BOOL blnitial. State, // начальное состояние события LPCTSTR lp. Name // имя события ); функции следую щий
Для перевода любого события в сигнальное состояние используется функция Set. Event, которая имеет следующий прототип: BOOL Set. Event( HANDLE h. Event // дескриптор события );
#include
// создаем события с автоматическим сбросом h. Out. Event = Create. Event(NULL, FALSE, NULL); if (h. Out. Event == NULL) return Get. Last. Error(); h. Add. Event = Create. Event(NULL, FALSE, NULL); if (h. Add. Event == NULL) return Get. Last. Error(); // создаем поток thread h. Thread = Create. Thread(NULL, 0, thread, NULL, 0, &IDThread); if (h. Thread == NULL) return Get. Last. Error(); // ждем, пока поток thread выполнит половину работы Wait. For. Single. Object(h. Out. Event, INFINITE);
// выводим значение переменной cout « "A half of the work is done. " « endl; cout « "Press any key to continue. " « endl; cin. get(); // разрешаем дальше работать потоку thread Set. Event (h. Add. Event) ; Wait. For. Single. Object(h. Thread, INFINITE); Close. Handle(h. Thread); Close. Handle(h. Out. Event); Close. Handle(h. Add. Event); cout « "The work is done. " « endl; return 0; }
Доступ к существующему событию можно открыть с помощью функции Create. Event или Open. Event. HANDLE Open. Event( DWORD dw. Desired. Access, // флаги доступа BOOL blnherit. Handle, // режим наследования LPCTSTR lp. Name // имя события );
Параметр dw. Desired. Access определяет доступ к событию и может быть равен любой логической комбинации следующих флагов: ü ЕVENT_ALL_АСCESS — полный доступ; ÜЕVENT_MODIFY_SТАТЕ— модификация состояния; ÜSYNCHRONIZE — синхронизация. Эти флаги устанавливают следующие режимы доступа к событию: ü флаг ЕVENT_ALL_АСCESS означает, что поток может выполнять над событием любые действия; ü флаг EVENT_MODIFY_STATE означает, что поток может использовать функции Set. Event и Reset. Event для изменения состояния события; ü флаг SYNCHRONIZE означает, что поток может использовать событие в функциях ожидания.
#include
// запускаем процесс, который ждет ввод символа Zero. Memory(&si, sizeof(STARTUPINFO)); si. cb = sizeof(STARTUPINFO); if (!Create. Process(sz. App. Name, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, &si, &pi)) return 0; // закрываем дескрипторы этого процесса Close. Handle(pi. h. Process); Close. Handle(pi. h. Thread); // ждем оповещение о наступлении события о вводе символа dw. Wait. Result = Wait. For. Singie. Object(hln. Event, INFINITE); if (dw. Wait. Result != WAIT_OBJECT_0) return dw. Wait. Result; cout « "A symbol has got. " « endl; Close. Handie (hln. Event) ; cout « "Press any key to exit. "; cin. get(); return 0; }
#include
cout « "Input any char: "; cin » c; // устанавливаем событие о вводе символа Set. Event(hln. Event); // закрываем дескриптор события в текущем процессе Close. Handie(hln. Event); cin. get(); cout << "Press any key to exit. " << endl; cin. get(); return 0; }
Семафоры в операционных системах Windows описываются объектами ядра semaphores. Семафор находится в сигнальном состоянии, если его значение больше нуля. В противном случае семафор находится в несигнальном состоянии. Создаются семафоры посредством вызова функции Create. Semaphore, которая имеет следующий прототип: HANDLE Create. Semaphore( LPSECURITY_ATTRIBUTES Ip. Semaphore. Attribute, // атрибуты защиты LONG llnitial. Count, // начальное значение семафора LONG IMaximum. Count, // максимальное значение семафора LPCTSTR lp. Name // имя семафора );
BOOL Release. Semaphore ( HANDLE h. Semaphore, // дескриптор семафора LONG IRelease. Count, // положительное число, на которое увеличивается значение семафора LPLONG lp. Previous. Count // предыдущее значение семафора );
HANDLE Open. Semaphore( DWORD dw. Desired. Access, // флаги доступа BOOLblnherit. Handle, // режим наследования LPCTSTR lp. Name // имя события ); Параметр dw. Desired. Access определяет доступ к семафору и может быть равен любой логической комбинации следующих флагов: ü s. EMAPHORE_ALL_ACCESS — полный доступ к семафору; ÜSEMAPHORE_MODIFY_STATE — изменение состояния семафора; ÜSYNCHRONIZE —синхронизация.
#include
int main() { int i; HANDLE h. Thread; DWORD IDThread; cout « "An initial state of the array: "; for (i = 0; i < 10; i++) cout « a[i] «' '; cout « endl; // создаем семафор h. Semaphore=Create. Semaphore(NULL/ 0, 10, NULL); if (h. Semaphore == NULL) return Get. Last. Error(); // создаем поток, который готовит элементы массива h. Thread = Create. Thread(NULL, 0, thread, NULL, 0, &IDThread); if (h. Thread == NULL) return Get. Last. Error();
// поток main выводит элементы массива только после их подготовки потоком thread cout « "A final state of the array: "; for (i = 0; i < 10; i++) { Wait. For. Single. Object(h. Semaphore, INFINITE); cout « a[i] « " a" « flush; } cout « endl; Close. Handle(h. Semaphore); Close. Handle(h. Thread); return 0; }