Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лаб-07(Shinhron) Синхронизация потоков.DOC
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
327.68 Кб
Скачать
  1. Синхронизация потоков с объектами ядра

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

Для синхронизации потока можно использовать следующие объекты ядра:

  1. Процессы

  1. Потоки

  1. Файлы

  1. Консольный ввод

  1. Уведомления об изменении файлов

  1. Объекты mutex

  1. Семафоры

  1. События (с автоматическим сбросом и сбросом вручную)

Каждый объект может находиться в одном из двух состояний: свободном (signaled) или занятом (nonsignaled). Потоки могут остановиться и ждать, пока какой-либо объект не освободится. Если, скажем, поток родительского процесса должен ждать завершения дочернего, его можно отправить "в спячку" до освобождения объекта ядра, идентифицирующего дочерний процесс.

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

Понятия "свободен-занят" аналогичны обыкновенному флагу. Потоки спят, пока ожидаемые ими объекты заняты (флаг опущен). Объект освободился (флаг поднят) - спящий поток замечает это, просыпается и возобновляет исполнение.

Некоторые из перечисленных объектов ядра существуют лишь для синхронизации потоков. Например, поток с описателем объекта "процесс" может вызвать любые Win32-функции, позволяющие изменить класс приоритета или получить код завершения процесса. Через этот же описатель поток может синхронизировать себя с завершением процесса. Описатель потока служит тем же целям: для управления потоком и его синхронизации с завершением другого потока.

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

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

Прочие объекты ядра: уведомления об изменении файлов, объекты mutex, семафоры и события - служат одной цели: синхронизации потоков. Ряд функций Win32, предназначенных специально для работы с этими объектами, позволяют создавать или открывать объекты, синхронизировать с ними потоки и закрывать их. Никакие другие операции с подобными объектами ядра не допускаются.

2.1. Объекты mutex

Эти объекты весьма похожи на критические разделы - за исключением того, что с их помощью можно синхронизировать доступ к данным со стороны нескольких процессов. Для использования объекта mutex один из процессов должен сначала создать его, вызвав функцию Сreate Mutex:

HANDLE CreateMutex (LPSECURITY_ATTRIBUTES lpsa, BOOL fInitialOwner,

LPTSTR lpszMutexName);

Параметр lpsa указывает на структуру SECURITY_ATRIBUTES. Параметр fInitialOwner определяет: должен ли поток, создающий mutex, быть первоначальным владельцем этого объекта. Если он равен TRUE, данный поток становится его владельцем, и, следовательно, объект mutex оказывается в занятом состоянии. Любой другой поток, ожидающий этот объект, будет приостановлен, пока первый поток не освободит его. Передача FALSE в параметре fInitialOwner подразумевает, что объект mutex не принадлежит ни одному из потоков и поэтому "рождается" свободным". Первый же поток из числа ожидающих этот объект может занять его и тем самым продолжить свое исполнение.

Параметр lpszMutexName содержит либо NULL, либо адрес строки (с нулевым символом в конце), идентифицирующий объект mutex. Когда приложение вызывает CreateMutex, система создает объект ядра "mutex" и присваивает ему имя, на которое указывает параметр lpszMutexName. Это имя используется при совместном доступе к нему нескольких процессов. CreatMutex возвращает описатель (его значение зависит от конкретного процесса), идентифицирующий новый объект mutex.

Одно из главных отличий объектов mutex от критических разделов в том, что первые способны синхронизировать потоки, выполняемые в разных процессах. С этой целью поток в каждом процессе должен располагать своим, специфичным для данного процесса описателем единственного объекта mutex. Эти описатели можно получить несколькими путями. Наиболее распространенный способ: один из потоков каждого процесса вы-зывает Creat-Mutex и передает ей в параметре lpszMutexName одну и ту же строку. Первый вызов функции приводит к созданию ядра "mutex", а остальные ее вызовы просто возвращают соответствующим потокам описатели этого объекта, значения которых специфичны для каждого процесса.

Определить, действительно ли CreatMutex создала новый объект mutex, можно через функцию GetLastError (но обращаться к ней нужно сразу после вызова CreatMutex). Если GetLastError возвратит ERROR_ALREADY_EXISTS, значит, новый объект mutex создан не был.

Еще один способ получить описатель mutex - вызвать OpenMutex:

HANDLE OpenMutex (DWRD fdwAccess, BOOL fInherit, LPTSTR lpszName);

Параметр fdwAccess может быть равен либо SYNCHRONIZE, либо MUTEX_ALL_ACCESS. Параметр fInherit определяет: должен ли любой порожденный процесс наследовать данный описатель данного объекта mutex. А параметр lpszName - это имя объекта mutex в виде строки с нулевым символом в конце.

При обращении к функции OpenMutex система сканирует все существующие объекты mutex, проверяя: нет ли среди них объекта с именем, указанным в параметре lpszName. Обнаружив такой объект, она создает описатель объекта, специфичный для данного процесса, и возвращает его вызвавшему потоку. В дальнейшем любой поток из данного процесса может использовать этот описатель для вызова любой функции, требующей такой описатель. А если объекта mutex с указанным именем нет, функция возвращает NULL.