Синхронизация с использованием процессов и потоков 


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



ЗНАЕТЕ ЛИ ВЫ?

Синхронизация с использованием процессов и потоков



Процессы и потоки можно использовать и для решения задач синхронизации.

Объект ядра «процесс» сразу после создания всегда находятся в занятом состоянии. В момент завершения процесса операционная система автоматически освобождает его объект ядра «процесс», и он навсегда остается в свободном состоянии.

Точно такие же правила распространяются и на объекты ядра «поток». Они тоже сразу после создания находятся в занятом состоянии. Когда поток завершается, операционная система автоматически переводит объект ядра «поток» в свободное состояние. Как и объект ядра «процесс», объект ядра «поток» никогда не возвращается в занятое состояние.

 

Объекты Mutex

Объекты ядра Mutex (мьютекс) гарантируют потокам взаимоисключающий доступ к единственному ресурсу. Мьютексы ведут себя точно так же, как и критические секции. Однако, если последние являются объектами пользовательского режима, то мьютексы — объекты ядра. Кроме того, объект Mutex позволяет синхронизировать доступ к ресурсу нескольких потоков из разных процессов; при этом можно задать максимальное время ожидания доступа к ресурсу.

Для использования объекта Mutex один из процессов должен сначала создать его вызовом CreateMutex:

function CreateMutex(lpMutexAttributes: PSecurityAttributes;

bInitialOwner: Boolean; lpName: PChar): THandle;

где lpMutexAttributes – указывает на структуру SECURITY_ATTRIBUTES, которая содержит информацию о защите объекта ядра «mutex». Если защиты не нужно в этот параметр заносится nil.

bInitialOwner – определяет начальное состояние мьютекса. Если в нем передается FALSE (что обычно и бывает), объект-мьютекс не принадлежит ни одному из потоков и поэтому находится в свободном состоянии. Если же в нем передается TRUE, мьютекс изначально находится в занятом состоянии.

lpName – указатель на сроку,, заканчивающуюся двоичным нулем и содержащую имя объекта «mutex». Применяется в тех случаях, когда объект «mutex» используется для синхронизации потоков разных процессов. Если мьютекс используется для синхронизации потоков одного процесса, этот параметр устанавливается в nil.

При успешном выполнении функция CreateMutex возвращает дескриптор мьютекса, В случае ошибки функция возвращает nil. Причем, если при вызове функции CreateMutex указывается имя мьютекса и объект мьютекс с таким именем уже существует, то функция вернет значение – nil, а функция GetLastError вернет значение - ERROR_ALREADY_EXISTS.

Любой процесс может получить свой («процессно-зависимый») дескриптор существующего объекта "мьютекс", вызвав функцию OpenMutex:

function OpenMutex(dwDesiredAccess: DWORD; bInheritHandle: Boolean;

lpName: PChar): THandle;

где dwDesiredAccess - определяет требуемый доступ к мьютексу. Возможные значения данного параметра приведены в таблице 7.1.

bInheritHandle - определяет тип наследования дескриптора. Если данный параметр имеет значение TRUE, процесс, создаваемый функцией CreateProcess, будет наследовать данный дескриптор. Если же параметр имеет значение FALSE, дескриптор мьютекса не будет наследуемым.

lpName - указатель на сроку, заканчивающуюся двоичным нулем и содержащую имя мьютекса.

 

Таблица 7.1 Значения параметра dwDesiredAccess

Значение Описание
MUTEX_ALL_ACCESS Означает все возможные флаги доступа для мьютекса
SYNCHRONIZE Допускается использование дескриптора объекта мьютекс в любой wait- функции для ожидания освобождения мьютекса

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

Если Wait -функция определяет, что мьютекс занят, то поток переходит в состояние ожидания освобождения объекта мьютекс.

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

function ReleaseMutex(hMutex: THandle): Boolean;

где hMutex – дескриптор объекта мьютекс.

При успешном выполнении функция ReleaseMutex возвращает значение TRUE.

Объект мьютекс отличается от остальных объектов ядра тем, что занявшему его потоку передаются права на владение им. Объекты мьютексы способны запоминать, какому потоку они принадлежат. Если какой-то посторонний поток попытается освободить мьютекс вызовом функции ReleaseMutex, то данная функция вернет FALSE. Вызов функции GetLastError даст значение ERROR_NOT_OWNER.

 

Семафоры

Объекты ядра «семафор» используются для учета ресурсов Как и все объекты ядра, они содержат счетчик числа пользователей, но, кроме того, поддерживают два 32 битных значения со знаком: одно определяет максимальное число ресурсов (контролируемое семафором), другое используется как счетчик текущего числа ресурсов

Для семафоров определены следующие правила:

· когда счетчик текущего числа ресурсов становится больше 0, семафор переходит в свободное состояние,

· если этот счетчик равен 0, семафор занят,

· система не допускает присвоения отрицательных значений счетчику текущего числа ресурсов;

· счетчик текущего числа ресурсов не может быть больше максимального числа ресурсов

Не следует путать счетчик текущего числа ресурсов со счетчиком числа пользователей объекта-семафора

Объект ядра «семафор» создается вызовом функции CreateSemapbore: function CreateSemaphore(lpSemaphoreAttributes: PSecurityAttributes;

lInitialCount, lMaximumCount: Longint; lpName: PChar): THandle;

где lpSemaphoreAttributes - указывает на структуру SECURITY_ATTRIBUTES, которая содержит информацию о защите объекта ядра «семафор». Если защиты не нужно в этот параметр заносится nil.

lInitialCount – счетчик текущего числа ресурсов.

lMaximumCount – максимальное число ресурсов, контролируемое семафором. Данный параметр должен быть больше 0.

lpName – указатель на строку, заканчивающуюся двоичным нулем и содержащую имя объекта «семафор». Применяется в тех случаях, когда объект «семафор» используется для синхронизации потоков разных процессов. Если объект «семафор» используется для синхронизации потоков одного процесса, этот параметр устанавливается в nil.

При успешном выполнении функции CreateSemaphore возвращает дескриптор семафора, В случае ошибки функция возвращает nil. Причем, если при вызове функции CreateSemaphore указывается имя семафора и объект «семафор» с таким именем уже существует, то функция вернет значение – nil, а функция GetLastError вернет значение - ERROR_ALREADY_EXISTS.

Любой процесс может получить свой («процессно-зависимый») дескриптор существующего объекта «семафор», вызвав функцию OpenSemaphore:

function OpenSemaphore(dwDesiredAccess: DWORD; bInheritHandle: Boolean;

lpName: PChar): THandle;

где dwDesiredAccess - определяет требуемый доступ к объекту «семафор». Возможные значения данного параметра приведены в таблице 7.2.

bInheritHandle - определяет тип наследования данного дескриптора. Если данный параметр имеет значение TRUE, процесс, создаваемый функцией CreateProcess, будет наследовать данный дескриптор. Если же данный параметр имеет значение FALSE, дескриптор семафора не будет наследуемым.

lpName - указатель на сроку, заканчивающуюся двоичным нулем и содержащую имя объекта «семафор».

 

Таблица 7.2 Значения параметра dwDesiredAccess

Значение Описание
SEMAPHORE_ALL_ACCESS Означает все возможные флаги доступа для объекта «семафор»
SEMAPHORE _MODIFY_STATE Объект «семафор» можно использовать только в функции ReleaseSemaphore для изменения счетчика текущего числа ресурсов
SYNCHRONIZE Допускается использование дескриптора объекта «семафор» в любой wait- функции для ожидания освобождения семафора.

Поток получает доступ к ресурсу, вызывая одну из Wait-функций и передавая ей дескриптор семафора. Wait-функция проверяет у семафора счетчик текущего числа ресурсов, если его значение больше 0 (семафор свободен), уменьшает значение этого счетчика на 1, и вызывающий поток остается планируемым

Если Wait - функция определяет, что счетчик текущего числа ресурсов равен 0 (семафор занят), система переводит вызывающий поток в состояние ожидания. Когда другой поток увеличит значение этого счетчика, система вспомнит о ждущем потоке и снова начнет выделять ему процессорное время (а он, захватив ресурс, уменьшит значение счетчика на 1).

Поток увеличивает значение счетчика текущего числа ресурсов, вызывая функцию ReleaseSemaphore:

function ReleaseSemaphore(hSemaphore: THandle; lReleaseCount:

Longint; lpPreviousCount: Pointer): Boolean;

Где hSemaphore – дескриптор семафора.

Данная функция складывает величину lReleaseCount со значением счетчика текущего числа ресурсов. Обычно в параметре lReleaseCount передают 1, но это вовсе не обязательно. Функция возвращает исходное значение счетчика ресурсов в lpPreviousCount.Если исходное значение счетчика в приложении не используется, то вместо параметра lpPreviousCountзаписывается значение nil.

При успешном выполнении функция ReleaseSemaphore возвращает значение TRUE.

События

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

Объекты-события обычно используют в том случае, когда какой-то поток выполняет инициализацию, а затем сигнализирует другому потоку, что тот может продолжить работу. Инициализирующий поток переводит объект «событие» в занятое состояние и приступает к своим операциям. Закончив, он сбрасывает событие в свободное состояние. Тогда другой поток, который ждал перехода события в свободное состояние, пробуждается и вновь становится планируемым.

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

1. Создание объекта ядра «событие», функция CreateEvent:

function CreateEvent(lpEventAttributes: PSecurityAttributes;

bManualReset, bInitialState: Boolean; lpName: PChar): THandle;

где lpEventAttributes - указывает на структуру SECURITY_ATTRIBUTES, которая содержит информацию о защите объекта ядра «событие». Если защиты не нужно в этот параметр заносится nil.

Пареметр fManualReset (булева переменная) определяет тип объекта «событие» - событие со сбросом вручную (TRUE) или с автосбросом (FALSE).

Параметру fInitialState определяет начальное состояние события — свободное (TRUE) или занятое (FALSE).

lpName – указатель на строку, заканчивающуюся двоичным нулем и содержащую имя объекта «событие». Применяется в тех случаях, когда объект «событие» используется для синхронизации потоков разных процессов. Если объект «событие» используется для синхронизации потоков одного процесса, этот параметр устанавливается в nil.

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

· вызовом CreateEvent с тем же параметром lpName;

· наследованием дескриптора;

· применением функции DuplicateHandle;

· вызовом OpenEvent c передачей в параметре lpName имени, совпадающего с указанным в аналогичном параметре функции CreateEvent.

2. Открытие объекта ядра «событие», функция OpenEvent:

function OpenEvent(dwDesiredAccess: DWORD; bInheritHandle: Boolean;

lpName: PChar): THandle;

где dwDesiredAccess - определяет требуемый доступ к объекту «событие». Возможные значения данного параметра приведены в таблице 7.3.

bInheritHandle - определяет тип наследования данного дескриптора. Если данный параметр имеет значение TRUE, процесс, создаваемый функцией CreateProcess, будет наследовать данный дескриптор. Если же данный параметр имеет значение FALSE, дескриптор события не будет наследуемым.

lpName – указатель на сроку, заканчивающуюся двоичным нулем и содержащую имя объекта «событие».

 

Таблица 7.3 Значения параметра dwDesiredAccess

Параметр dwDesiredAccess Описание
EVENT_ALL_ACCESS Означает все возможные флаги доступа для объекта «событие»
EVENT_MODIFY_STATE Объект событие можно использовать только в функциях SetEvent и ResetEvent для изменения состояния объекта «событие»
SYNCHRONIZE Допускается использование объекта «событие» в любой wait- функции для ожидания освобождения события.

 

3. Закрытие объекта ядра «событие».

Ненужный объект ядра «событие» следует закрыть вызовом CloseHandle.

4. Перевод события в свободное состояние

function SetEvent(hEvent: THandle): Boolean;

где hEvent – дескриптор события. При успешном выполнении функция возвращает ненулевое значение. В случае ошибки функция возвращает ноль.

5. Перевод события в занятое состояние

function ResetEvent(hEvent: THandle): Boolean;

где hEvent – дескриптор события.

При успешном выполнении функция возвращает ненулевое значение. В случае ошибки функция возвращает ноль.

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

 

Примеры работы с объектами синхронизации

Пример 1

Пример 1 (см. рисунок 7.1) демонстрирует работу таких объектов синхронизации, как критические секции, объекты mutex и события. В приложении также рассматривается работа функции WaitForMultipleObjects в режиме ожидания освобождения всех объектов и в режиме ожидания освобождения какого-либо одного объекта.

На форму переносятся командные кнопки Button1-Button6 и компонент Memo1.

Для программирования приложения выполняются следующие действия:

1. Определение глобальных переменных. Для этого в область VAR (после определения объекта Форма) добавляются следующие строки:

var

Form1: TForm1;

Sum:Integer; {Переменная, в которую записывается результат вычислений }

f:TextFile; {файловая переменная}

cs:TRTLCriticalSection; {критическая секция}

mt:THANDLE; {Дескриптор объекта mutex}

ev:THANDLE; {Дескриптор объекта «событие»}

 

2. Пример работы критических секций. Работа критических секций рассматривается на следующем примере – создается два дополнительных потока. Первый поток занимает критическую секцию, увеличивает число на 1 и освобождает критическую секцию. Второй поток записывает очередное число, сформированное первым потоком в файл. Программирование кнопки «Критические секции».

 

 

·

·

· Включите в текст приложения дополнительную функцию Thread1, которая выполняет вычисления (увеличивает число на 1):

procedure Thread1;

begin

While Sum<20 do

begin

EnterCriticalSection(cs);

Inc(Sum);

Sleep(30);

LeaveCriticalsection(cs);

end;

end;

· Включите в текст приложения дополнительную функцию Thread2, которая записывает в файл результат вычислений функции Thread1:

procedure Thread2;

begin

While Sum <20 do

begin

EnterCriticalSection(cs);

Writeln(f,IntToStr(Sum));

Sleep(30);

LeaveCriticalsection(cs);

end;

end;

· Для события OnClick кнопки «Критически секции» напишите следующий программный код:

procedure TForm1.Button1Click(Sender: TObject);

VAR hThread1,hThread2:THandle;

pFunc1,pFunc2:pointer;

ThreadID1,ThreadID2:CARDINAL;

begin

Sum:=0;

AssignFile(f,'C:\tempCS.txt');

Rewrite(f);

InitializeCriticalSection(cs);

pFunc1:=@Thread1;

pFunc2:=@Thread2;

hThread1:=CreateThread(nil,0,pFunc1,nil,0,ThreadID1);

hThread2:=CreateThread(nil,0,pFunc2,nil,0,ThreadID2);

While Sum<20 do

begin

EnterCriticalSection(cs);

Sleep(30);

LeaveCriticalSection(cs);

end;

ShowMessage ('Работа завершена');

CloseFile(f);

end;

3. Пример работы с объектом mutex. Работа объекта mutex рассматривается на примере аналогичном рассмотренному в п.2 (работа с критическими секциями). Создается два дополнительных потока. Первый поток занимает объект mutex, увеличивает число на 1, выводит его значение в поле Memo1 и освобождает объект mutex. Второй поток записывает очередное число, сформированное первым потоком в файл. Программирование кнопки «Mutex»:

· Включите в текст приложения дополнительную функцию Thread3, которая выполняет вычисления (увеличивает число на 1):

procedure Thread3;

begin

While Sum<20 do

begin

WaitForSingleObject(mt,INFINITE);

Inc(Sum);

Form1.Memo1.Lines.Add(IntToStr(sum));

Form1.Refresh;

ReleaseMutex(mt);

Sleep(20);

end;

end;

· Включите в текст приложения дополнительную функцию Thread4, которая записывает в файл результат вычислений функции Thread3:

procedure Thread4;

begin

While Sum <20 do

begin

WaitForSingleObject(mt,INFINITE);

Writeln(f,IntToStr(Sum));

ReleaseMutex(mt);

Sleep(20);

end;

CloseFile(f);

end;

· Для события OnClick кнопки «Mutex» напишите следующий программный код:

procedure TForm1.Button2Click(Sender: TObject);

VAR hThread3,hThread4:THandle;

pFunc3,pFunc4:pointer;

ThreadID3,ThreadID4:CARDINAL;

begin

Sum:=0;

Memo1.Clear;

// Form1.Refresh;

AssignFile(f,'C:\tempMT.txt');

Rewrite(f);

mt:=CreateMutex(nil,FALSE,PChar(''));

pFunc3:=@Thread3;

pFunc4:=@Thread4;

hThread3:=CreateThread(nil,0,pFunc3,nil,0,ThreadID3);

hThread4:=CreateThread(nil,0,pFunc4,nil,0,ThreadID4);

end;

4. Пример работы с объектом «событие с автосбросом». Работа объекта «событие» рассматривается на примере аналогичном рассмотренному в пп. 2 и 3. Создается два дополнительных потока. Первый поток занимает объект «событие», увеличивает число на 1, выводит его значение в поле Memo1 и освобождает объект «событие». Второй поток записывает очередное число, сформированное первым потоком в файл. Программирование кнопки «События»:

· Включите в текст приложения дополнительную функцию Thread5, которая выполняет вычисления (увеличивает число на 1):

procedure Thread5;

begin

While Sum<20 do

begin

WaitForSingleObject(ev,INFINITE);

Inc(Sum);

Form1.Memo1.Lines.Add(IntToStr(sum));

Form1.Refresh;

SetEvent(ev);

Sleep(20);

end;

end;

· Включите в текст приложения дополнительную функцию Thread6, которая записывает в файл результат вычислений функции Thread5:

procedure Thread6;

begin

While Sum <20 do

begin

WaitForSingleObject(mt,INFINITE);

Writeln(f,IntToStr(Sum));

SetEvent(ev);

Sleep(20);

end;

CloseFile(f);

end;

· Для события OnClick кнопки «События» напишите следующий программный код:

procedure TForm1.Button3Click(Sender: TObject);

VAR hThread5,hThread6:THandle;

pFunc5,pFunc6:pointer;

ThreadID5,ThreadID6:CARDINAL;

begin

Sum:=0;

Memo1.Clear;

Form1.Refresh;

AssignFile(f,'C:\tempEV.txt');

Rewrite(f);

ev:=CreateEvent(nil,FALSE,TRUE,PChar(''));

pFunc5:=@Thread5;

pFunc6:=@Thread6;

hThread5:=CreateThread(nil,0,pFunc5,nil,0,ThreadID5);

hThread6:=CreateThread(nil,0,pFunc6,nil,0,ThreadID6);

end;

5. Пример работы функции WaitForMultipleObjects, которая ожидает завершения всех запущенных процессов. В примере запускается три процесса «Калькулятор» и приложение ожидает завершения всех трех процессов, после чего работа приложения может быть продолжена. Для события OnClick кнопки «Ожидание завершения всех процессов» напишите следующий программный код:

procedure TForm1.Button5Click(Sender: TObject);

VAR StartInfo:TStartUpInfo;

ProcInfo:TProcessInformation;

h:PWOHandleArray;

i:Integer;

err:Boolean;

begin

err:=False;

For i:=0 to 2 do

begin

//Очистка полей структуры StartInfo

FillChar(StartInfo,Sizeof(StartInfo),#0);

StartInfo.cb:=Sizeof(StartInfo);

If not CreateProcess(nil,'C:\WINNT\system32\calc.exe',nil,nil,False,

NORMAL_PRIORITY_CLASS,nil,nil,StartInfo,ProcInfo)

then

begin

ShowMessage('Ошибка '+IntToStr(GetLastError));

err:=True;

end

else

begin

h[i]:=ProcInfo.hProcess;

end;

end;

If not err

then WaitFormultipleObjects(3,h,True,INFINITE);

end;

6. Пример работы функции WaitForMultipleObjects, которая ожидает завершения одного из запущенных процессов. В примере запускается три процесса «Калькулятор» и приложение ожидает завершения какого-либо одного процесса, после чего работа приложения может быть продолжена. Для события OnClick кнопки «Ожидание завершения одного из процессов» напишите следующий программный код:

procedure TForm1.Button6Click(Sender: TObject);

VAR StartInfo:TStartUpInfo;

ProcInfo:TProcessInformation;

h:PWOHandleArray;

i:Integer;

err:Boolean;

begin

err:=False;

For i:=0 to 2 do

begin

//Очистка полей структуры StartInfo

FillChar(StartInfo,Sizeof(StartInfo),#0);

StartInfo.cb:=Sizeof(StartInfo);

If not CreateProcess(nil,'C:\WINNT\system32\calc.exe',nil,nil,False,

NORMAL_PRIORITY_CLASS,nil,nil,StartInfo,ProcInfo)

then

begin

ShowMessage('Ошибка '+IntToStr(GetLastError));

err:=True;

end

else

begin

h[i]:=ProcInfo.hProcess;

end;

end;

If not err

then

WaitFormultipleObjects(3,h,False,INFINITE);

end;

7. Завершение работы приложения. Для события OnClick кнопки «Выход» напишите следующий программный код:

procedure TForm1.Button4Click(Sender: TObject);

begin

Form1.Close;

end;

Пример 2

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

Данное приложение работает следующим образом: имеется два потока Thread1 и Thread 2. Поток Thread2 принимает запрос клиента, поток Thread1 реализует процесс обслуживания очередного запроса на сервере. Количество одновременно обслуживаемых запросов регулируется семафором. Приложение, реализующее данную задачу изображено на рисунке 7.2.

Для создания данного выполните следующие действия:

1. Объявите глобальные переменные:

var

Form1: TForm1;

//Дескриптор потока, создающего поток Thread2

hThread2:THandle;

// Массив потоков обслуживания запросов клиентов

hThread1:Array[0..1000] of THandle;

// Идентификаторы соответствующих потоков

ThreadId2:DWORD;

ThreadId1:Array[0..1000] of DWORD;

// Дескриптор семафора

serv1:tHandle;

// Номер очередного поступившего запроса

q:Integer;

// Максимально допустимое число запросов

zapr:Integer;

2. Создайте процедуру Thread1, следующего вида:

 

 

procedure Thread1;

Var q1:Integer;

Begin

// Запомнить номер очередного обрабатываемого запроса

q1:=q;

// Если сервер свободен, запрос обрабатывается

WaitForSingleObject(serv1,INFINITE);

// Запрос обрабатывается при условии,

// что номер запроса - положительное число

If q1>0 then

Begin

// Случайное время выполнения звапроса

Sleep(1000+Random(2000));

// Вывод информации о завершении обработки запроса с номером q1

Form1.Memo1.Lines.Add('Обработан запрос номер '+IntToStr(q1));

end;

// Сервер освобождается для обработки других запросов

ReleaseSemaphore(serv1,1,nil);

end;

 

3. Создайте процедуру Thread1, следующего вида:

procedure Thread2;

begin

// Создается очередной запрос, если общее число созданных запросов

// меньше числа zapr, вводимого с клавиатуры

While q<zapr do

Begin

// Запросы поступают через случайный интервал времени

Sleep(100+Random(200));

// Увеличиваем на 1 число поступивших запросов

q:=q+1;

Form1.Edit3.Text:=IntToStr(q);

// Создаем поток, для обслуживания поступившего запроса

hThread1[q]:=CreateThread(nil,0,@Thread1,nil,0,ThreadID1[q]);

end;

end;

 

4. Для события OnClick кнопки Button1 введите следующий программный код:

procedure TForm1.Button1Click(Sender: TObject);

Var

res:Integer;

begin

Memo1.Clear;

// очищаем счетчик числа поступивших запросов

q:=0;

// ввод числа одновременно обслуживаемых на сервере запросов

// это число требуется для настройки семафора

res:=StrToInt(Edit1.text);

// ввод максимального числа запросов

zapr:=StrToInt(Edit2.text);

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

serv1:=CreateSemaphore(nil,res,res,nil);

// создание потока, предназначенного для приема запросов клиентов

hThread2:=CreateThread(nil,0,@Thread2,nil,0,ThreadID2);

end;

5. Для события OnClick кнопки Button2 введите следующий программный код:

procedure TForm1.Button2Click(Sender: TObject);

begin

Application.Terminate;

end;

 

6. Откомпилируйте приложение и проверьте его работу.

7.4. Задания для самостоятельной работы

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

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

3. Исследование возможности синхронизации потоков с помощью событий c автоматическим сбросом. Написать программу, состоящую из четырех потоков. Первый поток считывает порциями в буфер текстовый файл. Второй поток подсчитывает количество символов в файле. Третий поток подсчитывает количество слов в файле. Четвертый поток подсчитывает количество строк в файле. Работа всех потоков синхронизируется.

4. Исследование возможности синхронизации потоков с помощью критических секций. Создание многооконного приложения (MDI приложения). В каждом окне рисуются произвольные фигуры. Причем, в каждый момент времени может рисоваться фигура только в одном окне.

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

6. Исследование возможности синхронизации потоков с помощью семафоров. Напишите программу, использующую семафор для ограничения количества запущенных потоков, работающих в MDI-окне. Программа позволяет создавать произвольное количество окон, но работа может выполняться только в ограниченном количестве окон, в соответствии с показаниями семафора.

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

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

9. С помощью объектов синхронизации Windows смоделировать работу парикмахерской. В парикмахерской 3 кресла, 3 парикмахера, зал ожидания, в котором 4 клиента могут разместиться на диване, а остальные стоя. Правила пожарной безопасности ограничивают общее количество клиентов внутри посещения 20 людьми. Всего парикмахерская должна обслужить 50 клиентов.

10. С помощью объектов синхронизации Windows решить задачу об обедающих философах (задача Дейкстры). В некотором царстве, в некотором государстве жили вместе 5 философов. Жизнь их проходила в размышлениях, прерываемых приемами пищи. Философы давно сошлись во мнении, что только спагетти в состоянии восстановить их подточенные непрерывными размышлениями силы. Питались они за одним большим круглым столом, на котором в центре помещалось большое блюдо со спагетти, по окружность пять тарелок и пять вилок. Проголодавшийся философ садится на свое место за столом и, пользуясь двумя вилками, приступает к еде. Задача состоит в том, чтобы разработать алгоритм обеда, который обеспечивает взаимоисключения (два философа не могут одновременно пользоваться одной вилкой) и не допу скает взаимоблокировок.

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

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

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

14. Задача взаимного исключения. ОС, управляющая параллельными процессами, должна согласовать работу n>1 параллельных процессов при использовании некоторого критического ресурса таким образом, чтобы удовлетворить следующим требованиям: одновременно внутри критической области должно находиться не более одного процесса. Количество процессов заранее не известно.

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

· выполнять требования задачи взаимного исключения по отношению к критическому ресурсу — обобщенной памяти для хранения сообщения

· учитывать состояние обобщенной области памяти, характеризующей возможность или невозможность посылки (принятия) очередного сообщения

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

16. Задача "Читатели-писатели"(больший приоритет имеют процессы-читатели). В отношении некоторой области памяти, являющейся критическим ресурсом для параллельных процессов, работающих с ней, выделяется два типа процессов. Первый тип — процессы-читатели. Они считывают одновременно информацию из области, если это допускается при работе с конкретным устройством памяти. Второй тип — процессы-писатели. Они записывают информацию в область и могут делать это, только исключая как друг друга, т. е. запись должна удовлетворяться на основе решения задачи взаимного исключения. Устанавливается приоритетность в использовании критического ресурса процессам-читателям. Если хотя бы один процесс-читатель пользуется ресурсом, то он закрыт для использования всем процессам-писателям и доступен для использования всем процессам-читателям.

17. Задача "Читатели-писатели" (больший приоритет имеют процессы-писатели). В отношении некоторой области памяти, являющейся критическим ресурсом для параллельных процессов, работающих с ней, выделяется два типа процессов. Первый тип — процессы-читатели. Они считывают одновременно информацию из области, если это допускается при работе с конкретным устройством памяти. Второй тип — процессы-писатели. Они записывают информацию в область и могут делать это, только исключая как друг друга, т. е. запись должна удовлетворяться на основе решения задачи взаимного исключения. Устанавливается приоритетность в использовании критического ресурса процессам - писателями. При появлении запроса от процесса-писателя необходимо закрыть ресурс для использования всем процессам-читателям.

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

19. С помощью объектов синхронизации Windows смоделировать работу аэропорта. В приложении следует задавать число взлетно-посадочных полос и число самолетов, выполняющих взлет и посадку.

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

КОНТРОЛЬНЫЕ ВОПРОСЫ

1. Какие объекты ядра можно использовать как объекты синхронизации?

2. Когда объект ядра «процесс» находится в свободном, а когда в занятом состоянии?

3. Как потоки различных процессов могут использовать один и тот же объект семафор, мьютекс или событие для синхронизации?

4. Чем объект мьютекс отличается от других объектов синхронизации?

5. Чем объект семафор отличается от других объектов синхронизации?

6. В каких случаях следует использовать события со сбросом вручную, а в каких события с автосбросом?

 

ЛАБОРАТОРНАЯ РАБОТА № 8

СОЗДАНИЕ ДИНАМИЧЕСКИ ПОДКЛЮЧАЕМЫХ БИБЛИОТЕК

Цель работы

Целью данной работы является разработка динамически подключаемых библиотек (Dynamic-Link Libraries- DLL). В данной лабораторной работе рассматриваются следующие вопросы:

· Методы загрузки DLL (явная и неявная загрузки);

· Методы внедрения DLL в адресное пространство другого процесса.

 

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

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

· Kernel32.dll – управление памятью, процессами и потоками;

· User32.dll – поддержка пользовательского интерфейса, в том числе функции, связанные с созданием окон и передачей сообщений;

· GDI32.dll – графика и вывод текста;

· AdvAPI32.dll –функции для защиты объектов, работы с реестром и регистрации событий.

Библиотеки DLL функционируют в адресном пространстве приложения, загрузившего их, разделяя единую копию кода со всеми другими приложениями, использующими данную DLL. Приложения используют неявную (статическую) и явную (динамическую) загрузку DLL.

При неявной загрузке, в объявлении процедуры из внешней DLL включено ключевое слово external и имя DLL. В этом случае приложение пытается загрузить DLL до того, как будет вызван метод Application.Initialize, то есть до того, как приложение начнет выполняться.

При явной загрузке DLL, она будет загружаться в адресное пространство процесса в тот момент, когда будет вызван метод LoadLibrary.

Явная загрузка DLL требует от программиста большей аккуратности, но при этом сокращается время запуска приложения. Неявная загрузка DLL проще, так как при этом ОС Windows выполняет большую часть работы.

Создание DLL

Рассмотрим создание DLL на следующем примере:

Требуется создать динамически загружаемую библиотеку mess.dll, которая содержит:

1. процедуру HelloFromLib. Данная процедура выводит на экран число, переданное из приложения.

2. функцию fy=xn. Причем, x и n передаются как параметры.

Для со



Поделиться:


Последнее изменение этой страницы: 2017-02-06; просмотров: 356; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 3.149.243.32 (0.317 с.)