Объекты синхронизации и функции ожидания в Windows

Лабораторная работа № 3

СИНХРОНИЗАЦИЯ ПРОЦЕССОВ И ПОТОКОВ ПРИ ПОМОЩИ СЕМАФОРОВ И МЬЮТЕКСОВ

       Цели работы: научиться использовать функции Win32 API, предназначенные для синхронизации процессов и потоков в Windows.

       Задание на лабораторную работу

       1. Используйте семафоры или мьютексы для синхронизации потоков (процессов).

       2.Проанализируйте возможность использования семафоров (мьютексов) для синхронизации работы потоков (процессов) в процессе реализации задания на лабораторную работу. Обязательно использование либо семафора, либо мьютекса.

P.S. Более интересные варианты заданий – ближе к концу списка вариантов.

Вариант 1

       Написать программы для консольного процесса Boss (Резидент) и консольных процессов Scout (Шпион). Для моделирования передачи сообщений ввести специальные события, которые обозначают «точку» и «тире», конец сеанса.

       Процесс Boss :

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

       Процесс Scout:

  • запрашивает с консоли символы: «–», «.» (событие «тире», событие «точка») и передает соответствующие события процессу Boss;
  • завершает свою работу, когда будет введён символ, обозначающий конец ввода сообщений.

Вариант 2

       Написать программы для консольного процесса Boss (Резидент) и консольных процессов Scout (Шпион). Для моделирования передачи сообщений ввести специальные события, которые обозначают «1», «2» и конец сеанса для процессов Scout

       Процесс Boss :

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

Процесс Scout :

  • запрашивает с консоли сообщения, состоящее из «1», «2», и передает их (по одному) процессу Boss;
  • завершает свою работу

Вариант 3

       Написать программы для консольного процесса Boss (Резидент) и консольных процессов Scout (Шпион). Для моделирования передачи сообщений ввести специальные события, которые обозначают любые 4 цифры (на выбор).

       Процесс Boss:

  • запрашивает у пользователя количество процессов Scout, которые он должен запустить;
  • запрашивает у пользователя пароль (3 цифры);
  • запускает заданное количество процессов Scout;
  • принимает от каждого процесса Scout сообщение и выводит его на консоль в одной строке. Принимать сообщение может только от трёх процессов, передача остальных сообщений от других процессов должна блокироваться;
  • если приходит сообщение, с цифрой не из пароля, то выводит на консоль текст «ошибка»;
  •  завершает свою работу.

       Процесс Scout:

  • запрашивает с консоли сообщение, состоящее из цифр, и передает их (по одному) процессу Boss;
  • завершает свою работу.

Вариант 4

       Написать программы для консольного процесса Boss и консольных процессов Employee. Для моделирования передачи сообщений ввести специальные события, которые «0», «1», «2», «3» и конец сеанса для процессов Employee.

       Процесс Boss:

  • запрашивает у пользователя количество процессов Employee, которые он должен запустить;
  • запускает заданное количество процессов Employee;
  • принимает от каждого процесса Employee сообщение и выводит его на консоль в одной строке. Принимать сообщение может только от трёх процессов, передача остальных сообщений от других процессов должна блокироваться;
  • завершает свою работу.

       Процесс Employee:

  • запрашивает с консоли сообщения, состоящие из «0», «1», «2», «3», конец сеанса работы и передает(по одному) его процессу Boss;
  • завершает свою работу.

Вариант 5

       Написать программы для консольного процесса Administrator и процессов Writer. Для моделирования передачи сообщений ввести специальные события, которые обозначают сообщение «A», сообщение «B» и конец сеанса для процесса Writer.

       Одновременно принимать и отправлять сообщения может только один процесс Writer, передача остальных сообщений от других процессов должна блокироваться с помощью мьютексов;

       Процесс Administrator :

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

       Процесс Writer :

  • запрашивает с консоли сообщения, и передает их (по одному) процессу Administrator;
  • передает сообщение о завершении сеанса процессу Administrator;
  • завершает свою работу.

Вариант 6

       Написать программы для консольного процесса Boss и консольных процессов Employee. Для моделирования передачи сообщений ввести специальные события, которые «0», «1», «2», «3» и конец сеанса для процессов Employee.

       Процесс Boss :

  • запрашивает у пользователя количество процессов Employee, которые он должен запустить;
  • запускает заданное количество процессов Employee;
  • принимает от каждого процесса Employee сообщение и выводит его на консоль в одной строке. Принимать сообщение может только от трёх процессов, передача остальных сообщений от других процессов должна блокироваться;
  • завершает свою работу.

       Процесс Employee :

  • запрашивает с консоли сообщения, состоящее из «0», «1», «2», «3», конец сеанса работы и передает (по одному) его процессу Boss;
  • завершает свою работу.

Вариант 7

       Написать программы для консольного процесса Administrator и процессов Writer. Для моделирования передачи сообщений ввести специальные события, которые обозначают сообщение « + », сообщение «–», и конец сеанса для процесса Writer.

       Одновременно принимать и отправлять сообщения может только один процесс Writer, передача остальных сообщений от других процессов должна блокироваться с помощью мьютексов;

       Процесс Administrator :

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

       Процесс Writer :

  • запрашивает с консоли сообщения, и передает их (по одному) процессу Administrator;
  • передает сообщение о завершении сеанса процессу Administrator;
  • завершает свою работу.

Вариант 8

       Написать программы для консольного процесса Administrator и процессов Writer. Для моделирования передачи сообщений ввести специальные события, которые обозначают сообщение « + », сообщение «–», «*», «/» и конец сеанса для процесса Writer.

       Одновременно принимать и отправлять сообщения могут только два процесса Writer, передача остальных сообщений от других процессов должна блокироваться;

       Процесс Administrator :

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

       Процесс Writer :

  • запрашивает с консоли сообщения, и передает их (по одному) процессу Administrator;
  • передает сообщение о завершении сеанса процессу Administrator;
  • завершает свою работу.

Вариант 9

       Написать программы для консольного процесса Server и консольных процессов Client. Для моделирования передачи сообщений ввести специальные события, которые обозначают любые цифры.

       Процесс Server:

  • запрашивает у пользователя количество процессов Client, которые он должен запустить;
  • запрашивает у пользователя пароль (5 цифр и букв, другие символы вводить нельзя);
  • Если приходит сообщение, с цифрой или буквой не из пароля, то выводит на консоль текст «ошибка, неверный пароль»;
  • запускает заданное количество процессов Client;
  • принимает от каждого процесса Client сообщение и выводит его на консоль. Принимать сообщение может только от двух процессов, передача остальных сообщений от других процессов должна блокироваться;
  •  завершает свою работу.

       Процесс Client:

  • запрашивает с консоли сообщение, состоящее из цифр, и передает их процессу Server;
  • завершает свою работу.

Вариант 10

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

       Процесс Server:

  • запрашивает у пользователя количество процессов Client, которые он должен запустить;
  • запускает заданное количество процессов Client;
  • При первом подключении клиента к серверу, запрашивает у пользователя пароль (4 любых символа, заранее заданных);
  • Если приходит сообщение, с символом не из пароля, то выводит на консоль текст «ошибка, неверный пароль»;
  • принимает от каждого процесса Client, который ввел правильный пароль, сообщение и выводит его на консоль. Принимать сообщение может только от трех процессов одновременно, передача остальных сообщений от других процессов должна блокироваться, даже если пароль введен верно;
  •  завершает свою работу.

       Процесс Client:

  • запрашивает с консоли сообщение, состоящее из букв, и передает их (по одному) процессу Server;
  • передает пароль в начале;
  • завершает свою работу.

Вариант 11

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

       Процесс Server:

  • запрашивает у пользователя количество процессов Client, которые он должен запустить;
  • запускает заданное количество процессов Client;
  • при первом подключении клиента к серверу, запрашивает у пользователя пароль (4 любых символа, заранее заданных);
  • если приходит сообщение, с символом не из пароля, то выводит на консоль текст «ошибка, неверный пароль, вы сможете подключиться повторно через 40 секунд»;
  • добавляет запрет на подключение к серверу на 40 секунд;
  • принимает от каждого процесса Client, который ввел правильный пароль, сообщение и выводит его на консоль. Принимать сообщение может только от трех процессов одновременно, передача остальных сообщений от других процессов должна блокироваться, даже если пароль введен верно;
  • завершает свою работу.

       Процесс Client:

  • запрашивает с консоли сообщение, состоящее из цифр, и передает их (по одному) процессу Server;
  • передает пароль в начале;
  • завершает свою работу.

Вариант 12

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

       Процесс Server:

  • запрашивает у пользователя, хочет ли он задать пароль для сеанса, если да, то записывает введенный пароль;
  • запрашивает у пользователя количество процессов Client, которые он должен запустить;
  • запрашивает у пользователя пароль (любые символы либо ранее заданные, либо введенные пользователем в текущем сеансе);
  • если приходит сообщение, с символом не из пароля, то выводит на консоль текст «ошибка, неверный пароль»;
  • запускает заданное количество процессов Client;
  • принимает от каждого процесса Client, сообщение и выводит его на консоль. Принимать сообщение может только от двух процессов одновременно, передача остальных сообщений от других процессов должна блокироваться
  • завершает свою работу.

       Процесс Client:

  • запрашивает с консоли сообщение, состоящее из символов, и передает их (по одному) процессу Server;
  • завершает свою работу.

Вариант 13

Задача о napикмахере. В тихом городке есть парикмахерская. Салон парикмахерской мал.ходить там может только парикмахер и один посети­тель. Парикмахер всю жизнь обслуживает посетителей. Когда в салоне никого нет, он спит в кресле. Когда посетитель приходит и видит спящего парикмахера, он будет его, садится в кресло и спит, пока парикмахер занят стрижкой. Если посетитель приходит, а парикмахер занят, то он встает в очередь и засыпает. После стрижки парикмахер сам провожает посетителя. Если есть ожидающие посетители, то парикмахер будит одного из них, и ждет пока тот сядет в кресло парикмахера и начинает стрижку. Если никого нет, он снова садится в свое кресло и засыпает до прихода посетителя. Создать многопоточное приложение, моделирующее рабочий день парикмахерской.

Вариант 14

Задача о Винни-Пухе или правильные пчелы. В одном лесу живут n пчел и один медведь, которые используют один горшок меда, вместимостью Н глотков. Сначала горшок пустой. Пока горшок не наполнится, медведь спит. Как только горшок заполняется, медведь просыпается и съедает весь мед, после чего снова засыпает. Каждая пчела многократно собирает по од­ному глотку меда и кладет его в горшок. Пчела, которая приносит послед­нюю порцию меда, будит медведя. Создать многопоточное приложение, моделирующее поведение пчел и медведя. Условную синхронизацию потоков выполнить с помощью событий и семафоров.

 

Вариант 15

Задача об обедающих философах. Пять философов сидят возле круг­лого стола. Они проводят жизнь, чередуя приемы пищи и размышления. В центре стола находится большое блюдо спагетти. Спагетти длинные и запу­танные, философам тяжело управляться с ними, поэтому каждый из них.чтобы сьесть порцию, должен пользоваться двумя вилками. К несчастью, философам дали только пять вилок. Между каждой парой философов лежит одна вилка, поэтому эти высококультурные и предельно вежливые люди догово­рились, что каждый будет пользоваться только теми вилками, которые лежат рядом с ним (слева и справа). Написать многопоточную программу, модели­рующую поведение философов с помощью семафоров. Программа должна избегать фатальной ситуации, в которой все философы голодны, но ни один из них не может взять обе вилки (например, каждый из философов держит по одной вилки и не хочет отдавать ее). Решение должно быть симметричным, то есть все потоки-философы должны выполнять один и тот же код.

Вариант 16

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

Вариант 17

Задача о курильщиках. Есть три процесса-курильщика и один про­цесс-посредник. Курильщик непрерывно скручивает сигареты и курит их. Чтобы скрутить сигарету, нужны табак, бумага и спички. У одного процесса-курильщика есть табак, у второго - бумага, а у третьего - спички. Посредник кладет на стол по два разных случайных компонента. Тот процесс-курильщик, у которого есть третий компонент, забирает компоненты со сто­ла, скручивает сигарету и курит. Посредник дожидается, пока курильщик за­кончит, затем процесс повторяется. Создать многопоточное приложение, моделирующее поведение курильщиков и посредника.

Вариант 18

Задача о супермаркете. В супермаркете работают два кассира, по­купатели заходят в супермаркет, делают покупки и становятся в очередь к случайному кассиру. Пока очередь пуста, кассир спит, как только появляется покупатель, кассир просыпается. Покупатель спит в очереди, пока не подой­дет к кассиру. Создать многопоточное приложение, моделирующее рабочий день супермаркета.

Вариант 19

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

Вариант 20

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

Вариант 21

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

Вариант 22

Задача о гостинице-2 (умные клиенты). В гостинице 3 номера с ценой 200 рублей, 5 номеров с ценой 400 рублей и 2 номера с ценой 600 руб. Клиент, зашедший в гостиницу, обладает некоторой суммой и получает номер по своим финансовым возможностям, если тот свободен. Если среди доступных клиенту номеров нет свободных, клиент уходит искать ночлег в другое место. Создать многопоточное приложение, моделирующее работу гостиницы.

Вариант 23

Задача о гостинице - 3 (дамы и джентльмены). В гостинице 8 но­меров рассчитаны на одного человека и 7 номеров рассчитаны на двух че­ловек. В гостиницу приходят клиенты дамы и клиенты джентльмены, и ко­нечно они могут провести ночь в номере только с представителем своего по­ла. Если для клиента не находится подходящего номера, он уходит искать ночлег в другое место. Создать многопоточное приложение, моделирующее работу гостиницы.

Теоретические сведения

Объекты синхронизации и функции ожидания в Windows

       Определение. Объектами синхронизации называются объекты ядра, которые могут находиться в одном из двух состояний: сигнальном(signaled) и несигнальном(nonsignaled).

       Можно выделить три класса объектов синхронизации:

       К первому классу относятся объекты, которые служат только для решения проблемы синхронизации параллельных потоков. К таким объектам синхронизации в Windows относятся :

       – мьютекс(mutex);

       – событие(event);

       – семафор(semaphore).

       Ко второму классу относится ожидающий таймер (waitabletimer).

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

Мьютексы в Windows

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

       Создается мьютекс вызовом функции CreateMutex, которая имеет следующий прототип :

       HANDLE CreateMutex(

                   LPSECURITY_ATTRIBUTES lpMutexAttributes, // атрибутызащиты

                   BOOL bInitialOwner, // начальныйвладелецмьютекса

                   LPCTSTR lpName // имямьютекса

                   );

       ПоказначениепараметраLPSECURITY_ATTRIBUTESбудемустанавливатьвNULL. Это означает, что атрибуты защиты заданы по умолчанию, то есть дескриптор мьютекса не наследуется, и доступ к мьютексу имеют все пользователи.

       Для того чтобы получить доступ к уже созданному мьютексу, поток может также использовать функцию OpenMutex, которая имеет следующий прототип:

       HANDLEOpenMutex(

                   DWORDdwDesiredAccess, // доступкмьютексу

                   BOOLbInheritHandle // свойствонаследования

                   LPCTSTRlpName // имямьютекса

                   );

Пример 1. Использование мьютекса для синхронизации потоков.

// Несинхронизированные потоки, выполняющиеся в разных процессах

#include <windows.h>

#include <iostream>

                   using namespace std;

       int main()

       {

                   inti, j;

                   for (j = 10; j < 20; j++)

                   {

                              for (i = 0; i< 10; i++)

                              {

                                          cout<< j << ' ';

                                          cout.flush();

                                          Sleep(5);

                              }

                              cout<<endl;

                   }

                   return 0;

       }

Пример 2. Использование мьютекса для синхронизации процессов.

// Несинхронизированные потоки, выполняющиеся в разных процессах

#include <windows.h>

#include <iostream>

                   using namespace std;

       int main()

       {

                   charlpszAppName[] = "D:\\os.exe";

                   STARTUPINFO si;

                   PROCESS_INFORMATION pi;

                   ZeroMemory(&si, sizeof(STARTUPINFO));

                   si.cb = sizeof(STARTUPINFO);

                   // создаемновыйконсольныйпроцесс

                   if (!CreateProcess(lpszAppName, NULL, NULL, NULL, FALSE,

                              NULL, NULL, NULL, &si, &pi))

                   {

                              cout<< "The new process is not created." <<endl;

                              cout<< "Press any key to exit." <<endl;

                              cin.get();

                              returnGetLastError();

                   }

                   // выводим на экран строки

                   for (int j = 0; j < 10; j++)

                   {

                              for (inti = 0; i< 10; i++)

                              {

                                          cout<< j << ' ';

                                          cout.flush();

                                          Sleep(10);

                              }

                              cout<<endl;

                   }

                   // ждем пока дочерний процесс закончит работу

                   WaitForSingleObject(pi.hProcess, INFINITE);

                   // закрываем дескрипторы дочернего процесса в текущем процессе

                   CloseHandle(pi.hThread);

                   CloseHandle(pi.hProcess);

                   return 0;

       }

Пример 3. Синхронизация потоков, выполняющихся в разных процессах, с использованием мьютекса.

#include <windows.h>

#include <iostream>

       using namespace std;

       int main()

       {

                   HANDLE hMutex;

                   inti, j;

                   // открываеммьютекс

                   hMutex = OpenMutex(SYNCHRONIZE, FALSE, "DemoMutex");

                   if (hMutex == NULL)

                   {

                              cout<< "Open mutex failed." <<endl;

                              cout<< "Press any key to exit." <<endl;

                              cin.get();

                              returnGetLastError();

                   }

                   for (j = 10; j < 20; j++)

                   {

                              // захватываеммьютекс

                              WaitForSingleObject(hMutex, INFINITE);

                              for (i = 0; i< 10; i++)

                              {

                                          cout<< j << ' ';

                                          cout.flush();

                                          Sleep(5);

                              }

                              cout<<endl;

                              // освобождаеммьютекс

                              ReleaseMutex(hMutex);

                   }

                   // закрываем дескриптор объекта

                   CloseHandle(hMutex);

                   return 0;

       }

События

       Определение.События – разновидность объектов ядра.Они содержат счетчик числа пользователей(как и все объекты ядра) и две булевы пере - менные: одна сообщает тип данного объекта - события, другая – его со - стояние(свободен или занят).

       События просто уведомляют об окончании какой - либо операции.Объекты - события бывают двух типов: со сбросом вручную(manual - resetevents) и с автосбросом(auto - resetevents).Первые позволяют возобновлять выполнение сразу нескольких ждущих потоков, вторые – только одного.

       Объект ядра «событие» создается функцией CreateEvent :

       HANDLE CreateEvent(

                   PSECURITY_ATTRIBUTES psa,

                   BOOL fManualReset,

                   BOOL fInitialState,

                   PCTSTR pszName);

       Параметр fManualReset(булева переменная) сообщает системе, какое событие следует создать – со сбросом вручную(TRUE) или с автосбросом(FALSE).Параметр fInitialState определяет начальное состояние события – свободное(TRUE) или занятое(FALSE).После того как система создает объект «событие», CreateEvent возвращает описатель события, специфичный для конкретного процесса.

       Для событий с автосбросом действует следующее правило.Когда его ожидание потоком успешно завершается, этот объект автоматически сбрасывается в занятое состояние.

Пример 4. Синхронизация потоков с использованием объекта ядра «событие».

                   // глобальное описание события со сбросом вручную (в занятом состоянии)

                   HANDLE g_hEvent;

       int WINAPI WinMain() {

                   // создаем объект "событие со сбросом вручную (взанятомсостоянии) g_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);

                   // создаем три новых потока

                   HANDLE hThread[3];

                   DWORD dwThreadTD;

                   hThread[0] = _beginthreadex(NULL, 0, WordCount, NULL, 0, &dwThreadlD); hThread[1] = _beginthreadex(NULL, 0, SpellCheck, NULL, 0, &dwThreadID); hTbread[2] = _beginthreadex(NULL, 0, GrarrmarCheck, NULL, 0, &dwThreadID);

                   OpenFileAndReadContentsIntoMemory();

                   // разрешаемвсемтремпотокамобращатьсякпамяти

SetEvent(g__hEvent),

       }

       DWORDWINAPIWordCount(PVOIDpvParam) { // ожиданиезагрузкиданныхизфайлавпамятьWaitForSingleObject(g_hEvent, INFINITE); // обращениекблокупамятиreturn(0); }

                   DWORD WINAPI SpellCheck(PVOID pvParam) {

                              // ожиданиезагрузкиданныхизфайлаWaitForSingleObject(g_hFvent, INFINITE);

                              // обращание к блоку памяти

                              return(0

                   };

       }

       DWORD WINAPI GrammarCheck(PVOID pvParam) {

                   // ждем, когдавпамятьбудутзагруженыданныеизфайлаWaitForSingleObject(g_hFvent, INFINITE);

                   // обращение к блоку памяти

                   return(0);

       }

       При запуске этот процесс создает занятое событие со сбросом вручную и записывает его описатель в глобальную переменную.

Пример 5. Использование обьектов - событий для синхронизации задач, принадлежащих различным процессам.Для этого создадим два приложения.Первое приложение СЕРВЕР, запускается первым и его задача заключается в том, чтобы следить за работой второго приложения КЛИЕНТ.Приложение КЛИЕНТ позволяет вводить с помощью клавиатуры и отображать в своем окне произвольные символы.Каждый раз, когда пользователь вводит в окне приложения КЛИЕНТ какой - либо символ, в окне контролирующего приложения СЕРВЕР отображается символ "*".

Приложение «КЛИЕНТ»

#include<windows.h>

#include <stdio.h>

#include <conio.h>

                   intmain()

       {

                   // Открываем событие Start, созданное сервером

                   HANDLE hStart = OpenEvent(

                              EVENT_ALL_ACCESS, // Типдоступаксобытию

                                                                             // (полный доступ)

                              TRUE,

                              START // Имя события

                              );

                   // Если событие не открылось успешно, то сервер не запущен

                   if (!hStart)

                   {

                              printf("No connection...\n");

                              return -1;

                   }

                   // Событие Press будет сообщать серверу о нажатии клавиши

                   HANDLE hPress = CreateEvent(

                              NULL, // Атрибуты безопасности (как и у процесса)

                              FALSE, // Ручной сброс, т.е. будет ли событие автоматически

                                          // переходить в несигнальное состояние

                                          // после удачного вызова

                                          // WaitForSingleObject и т.п.

                                          // или его надо сбросить вручную

                                          // (автоматический сброс)

                              FALSE, // Начальное состояние (несигнальное)

                              PRESS // Имя

                              );

                   // Событие Show будет сообщать клиенту о печати сервером '*'

                   HANDLE hShow = OpenEvent(EVENT_ALL_ACCESS, TRUE, SHOW);

                   // Событие Work будет сообщать серверу о том, что клиент еще работает

                   // по окончании работы клиент сбрасывает Work в несигнальное состояние

                   // и сервер завершает работу

                   // Событие Work создается без автопереключения (2-й параметр)

                   HANDLE hWork = CreateEvent(NULL, TRUE, TRUE, WORK);

                   // Говорим серверу, что готовы к работе, т.е.

                   // устанавливаем событие Start в сигнальное состояние

                   SetEvent(hStart);

                   // Событие Start нам больше не нужно, закрываем его

                   CloseHandle(hStart);

                   // Цикл считывания нажатия клавиш, пока не нажата кнопка Esc (Code = 27)

                   BYTE ch;

                   while ((ch = LOBYTE(LOWORD(getch()))) != 27)

                   {

                              if (ch == 13) // Нажат Enter (Code = 13)

                              {

                                          printf("\n");

                              }

                              else

                              {

                                          printf("%c", ch);

                              }

                              // Говорим серверу, что кнопка нажата

                              SetEvent(hPress);

                              // Ждем пока сервер не отобразит '*'

                              WaitForSingleObject(hShow, INFINITE);

                   }

                   // Сбрасываем (переводим в несигнальное состояние) событие Work

                   // тем самым предлагаем серверу завершить работу

                   ResetEvent(hWork);

                              // В данный момент сервер все еще ждет события Press

                              SetEvent(hPress);

                   // Закрываем созданные и открытые события

                   CloseHandle(hPress);

                   CloseHandle(hShow);

                   CloseHandle(hWork);

                   return 0;

       }

Приложение «СЕРВЕР»

#include <windows.h>

#include <stdio.h>

#include <conio.h>

                   intmain()

       {

                   // Создаем событие Start в несигнальном состоянии

                   HANDLE hStart = CreateEvent(NULL, FALSE, FALSE, START);

                   // Создаем событие Show, которое будет говорить клиенту о том, что

                   // сервернапечаталсимвол '*'

                   HANDLE hShow = CreateEvent(NULL, FALSE, FALSE, SHOW);

                   // Ожидаем, пока клиент не переведет событие Start в сигнальное состояние,

                   // тем самым подтвердит свою готовность к работе

                   printf("Wait...");

                   WaitForSingleObject(hStart, INFINITE);

                   printf("\n");

                   // Открываем события созданные клиентом для синхронизации

                   // Press – кнопка нажата, нужно вывести '*'

                   // Work – пока это событие в сигнальном состоянии

                   // клиент находится на связи, иначе – завершаем работу

                   HANDLE hPress = OpenEvent(EVENT_ALL_ACCESS, TRUE, PRESS);

                   HANDLE hWork = OpenEvent(EVENT_ALL_ACCESS, TRUE, WORK);

                          // Пока клиент на связи,

                              // т.е. событие Work находится в сигнальном состоянии

                              while (WaitForSingleObject(hWork, 0) == WAIT_OBJECT_0)

                              {

                                          // Ожидаемнажатиекнопки, отклиента

                                          WaitForSingleObject(hPress, INFINITE);

                                          printf("*");

                                          // Говорим, чтовывелизвездочкунаэкран

                                          SetEvent(hShow);

                              }

                   printf("\nExit\n");

                   // Закрываемоткрытыесобытия

                   CloseHandle(hStart);

                   CloseHandle(hPress);

                   CloseHandle(hShow);

                   CloseHandle(hWork);

                   return 0;

       }

Семафоры

       Определение.Семафор – неотрицательная целая переменная, значение которой может изменяться только при помощи неделимых операций.

Пример 5. Использование бинарного семафора для моделирования критических секций и событий.

       semaphor s = 1; // семафорсвободен

       void thread_1() void thread_2()

       {

                   {

                              . . .

                                          . . .

                                          . . .

                                          P(s); P(s);

                              if (n % 2 == 0) n++;

                              28

                                          n = a; V(s);

                              else

                                          n = b;

                              V(s);

                              ... }

                   ...

                              ...

       }

       Как следует из определения операций над семафором, данный подход решает проблему взаимного исключения одновременного доступа к переменной n для потоков thread_1 и thread_2.Таким образом, бинарный семафор позволяет решить проблему взаимного исключения.

       Теперь предположим, что поток thread_1 должен производить проверку значения переменной n только после того, как поток thread_2 увеличит значение этой переменной.Для решения этой задачи модифицируем наши программы следующим образом:

       semaphors = 0; // семафорзанят

       void thread_1() void thread_2()

       {

                   {

                              . . .

                                          . . .

                                          . . .

                                          P(s); n++;

                              if (n % 2 == 0) V(s);

                              n = a;

                              else .

                                          n = b;

                              ... }

                   ...

                              ...

       }

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

Семафоры в Windows

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

       HANDLE CreateSemaphore(

                   LPSECURITY_ATTRIBUTES lpSemaphoreAttribute, // атрибутыза-щиты

                   LONG lInitialCount, // начальное значение семафора

                   LONG lMaximumCount, // максимальное значение семафора

                   LPCTSTR lpName // имя семафора

                   );

Пример 6. Считающий семафор используется для синхронизации работы потоков.Для этого сначала рассмотрим несинхронизированный вариант этой программы.

// Несинхронизированные потоки

#include <windows.h>

#include <iostream>

       using namespace std;

       volatileint a[10];

       DWORD WINAPI thread(LPVOID)

       {

                   inti;

                   for (i = 0; i< 10; i++)

                   {

                              a[i] = i + 1;

                              Sleep(17);

                   }

                   return 0;

       }

       int main()

       {

                   inti;

                   HANDLE hThread;

                   DWORD IDThread;

                   cout<< "An initial state of the array: ";

                   for (i = 0; i< 10; i++)

                              30

                              cout<< a[i] << ' ';

                   cout<<endl;

                   // создаем поток, который готовит элементы массива

                   hThread = CreateThread(NULL, 0, thread, NULL, 0, &IDThread);

                   if (hThread == NULL)

                              returnGetLastError();

                   // поток main выводитэлементымассива

                   cout<< "A modified state of the array: ";

                   for (i = 0; i< 10; i++)

                   {

                              cout<< a[i] << ' ';

                              cout.flush();

                              Sleep(17);

                   }

                   cout<<endl;

                   CloseHandle(hThread);

                   return 0;

       }

       Теперь кратко опишем работу этой программы.Поток thread последовательно присваивает элементам массива «a» значения, которые на единицу больше, чем их индекс.

       Поток main последовательно выводит элементы массива «a» на консоль.Так как потоки thread и main не синхронизированы, то неизвестно, какое состояние массива на консоль поток main.Наша задача состоит в том, чтобы поток main выводил на консоль элементы массива «a» сразу после их подготовки потоком thread.Для этого мы используем считающий семафор.Следующая программа показывает, как этот считающий семафор используется для синхронизации работы потоков.

// Пример синхронизации потоков с использованием семафора

#include <windows.h>

#include <iostream>

       using namespace std;

       volatileint a[10];

       HANDLE hSemaphore;

       DWORD WINAPI thread(LPVOID)

       {

                   inti;

                   for (i = 0; i< 10; i++)

                              {

                                          a[i] = i + 1;

                                          // отмечаем, что один элемент готов

                                          ReleaseSemaphore(hSemaphore, 1, NULL);

                                          Sleep(500);

                              }

                   return 0;

       }

       int main()

       {

                   inti;

                   HANDLE hThread;

                   DWORD IDThread;

                   cout<< "An initial state of the array: ";

                   for (i = 0; i< 10; i++)

                              cout<< a[i] << ' ';

                   cout<<endl;

                   // создаемсемафор

                   hSemaphore = CreateSemaphore(NULL, 0, 10, NULL);

                   if (hSemaphore == NULL)

                              returnGetLastError();

                   // создаем поток, который готовит элементы массива

                   hThread = CreateThread(NULL, 0, thread, NULL, 0, &IDThread);

                   if (hThread == NULL)

                              returnGetLastError();

                   // поток main выводит элементы массива

                   // только после их подготовки потоком thread

                   cout<< "A final state of the array: ";

                   for (i = 0; i< 10; i++)

                   {

                              WaitForSingleObject(hSemaphore, INFINITE);

                              cout<< a[i] << ' ';

                              cout.flush();

                   }

                   cout<<endl;

                   CloseHandle(hSemaphore);

                   CloseHandle(hThread);

                   return 0;

       }


Дата добавления: 2018-05-13; просмотров: 962; Мы поможем в написании вашей работы!

Поделиться с друзьями:




Мы поможем в написании ваших работ!