Главная
страница 1
Белгородский государственный технологический университет

им. В.Г. Шухова

Институт информационных технологий и управляющих систем
кафедра: Программное обеспечение

вычислительной техники и

автоматизированных систем
дисциплина: «Операционные системы»

Курсовая работа

на тему:
Разработка программного обеспечения для решения задачи «обедающих философов» с использованием синхронизации с помощью семафоров”


Выполнил:

Юсюмбели Николай

группа ПВ-32.
Проверил:

к. т. н., доцент

Михелев В.М.


Белгород 2012

Оглавление


Введение 3

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

1.1 Методы синхронизации 5

1.2 Синхронизация потоков, классические задачи 7

2. Постановка задачи. 9

Проблемы 9

Решение задачи. Иерархия ресурсов. 10

3. Использованные API-функции 12

4. Модульная схема 15

Описание основного модуля Unit1.pas 15

Описание модуля Filosof.pas 16

5. Результаты тестирования программы 17

Заключение 21

Использованная литература 22

Приложение 23

Основной модуль Filosof.pas 23

unit Unit1 25

unit Unit2 28




Введение

Данная работа посвящена изучению основ работы с потоками в операционной системе Windows и методам их синхронизации.

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

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

При многопоточной организации работы приложения необходимо уметь

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

Этим обусловлена актуальность проблемы, рассматриваемой в данной курсовой работе. На сегодняшний день все современные операционные системы используют алгоритмы и методы синхронизации процессов и потоков, и я попробую решить одну из таких задач – задачу «Обедающих философов».

Целью данной работы является изучение способов создания и синхронизации потоков при помощи семафоров. Мною была предпринята попытка решения одного из самых распространенных проблем при создании многопоточного приложения – проблемы взаимной блокировки потоков (deadlock).

Объектом исследования в данной курсовой работе является API (application programming interfaces – интерфейс программирования приложений) операционных систем семейства Microsoft Windows. Предметом исследования являются объекты ядра ОС Windows, в частности процессы, потоки и семафоры.

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

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

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

В четвертой главе приведена модульная схема проекта и описания основных модулей.

В пятой главе приведены результаты разработанной программы, а также интерфейс приложения.

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

При выполнении данной работы, я опирался на книгу: Назарр К., Рихтер Дж. «Windows via C/C++». Изд. Питер, 2009 г. , 896 стр. Данная книга посвящена технологии разработки приложений в 32- и 64-разрядных версиях операционных систем Windows XP и Windows Vista с использованием функций Windows API. Детально раскрываются все важные аспекты работы с ядром ОС Windows.



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


Поток (thread) определяет последовательность исполнения кода в процессе. При инициализации процесса система всегда создает первичный поток. Начинаясь со стартового кода из библиотеки С/С++, который в свою очередь вызывает входную функцию (__tmain, _tWinMain) из вашей программы, он живет до того момента, когда входная функция возвращает управление стартовому коду и тот вызывает функцию ExitProcess. Большинство приложений обходится единственным, первичным потоком. Однако процессы могут создавать дополнительные потоки, что позволяет им эффективнее выполнять свою работу.

Однако, потоки могут выполняться параллельно до тех пор, пока не возникнет потребность в общении между ними. Для обеспечения такого рода взаимодействия необходимо специальным образом планировать и управлять ими в составе любой ОС. Специальные средства ОС устанавливают определенные ограничения на последовательность (очередность) выполнения взаимосвязанных параллельных процессов, обеспечения тем самым их синхронизацию и общение. В каждом конкретном случае синхронизация задается с помощью синхронизирующих правил, которые устанавливаются системой между процессами и определяют порядок их выполнения с целью обеспечения должного взаимодействия. Реализация синхронизирующих правил осуществляется с помощью механизмов синхронизации. Такие механизмы весьма многочисленны по способам реализации, отличаются степенью эффективности и областями использования в различных ОС.

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

1.1 Методы синхронизации



Критические секции

Один из методов синхронизации потоков состоит в использовании критических секций (critical sections). Это единственный метод синхронизации потоков, который не требует привлечения ядра Windows. (Критическая секция не является объектом ядра). Однако этот метод может использоваться только для синхронизации потоков одного процесса.

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

Мьютекс (MUTual Exclusions— взаимоисключения) - это объект ядра, который можно использовать для синхронизации потоков из разных процессов. Он может принадлежать или не принадлежать некоторому потоку. Если мьютекс принадлежит потоку, то он находится в состоянии «занято». Если данный объект не относится ни к одному потоку, то он находится в состоянии «свободно». Другими словами, принадлежать для него означает быть в состоянии «занято».

Если мьютекс не принадлежит ни одному потоку, первый поток, который вызовет функцию WaitForSingleObject, завладевает данным объектом, и тот переходит в состояние «занято». В определенном смысле мьютекс похож на выключатель, которым может пользоваться любой поток по принципу «первым пришел - первым обслужили» (first-come-first,-served).

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

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

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

Функция ReleaseSemaphore() используемая для увеличения значения счетчика семафора имеет больше параметров, чем ее "коллега" ReleaseMutex(). Объявление функции ReleaseSemaphore() выглядит следующим образом:

function ReleaseSemaphore(hSemaphore: THandle; IReleaseCount: Longint; IpPreviousCount: Pointer): BOOL; stdcall;

С помощью параметра IReleaseCount можно задать число, на которое будет увеличено значение счетчика семафора. При этом старое значение счетчика будет сохранено в переменной типа Longint, на которую указывает параметр IpPreviousCount, если его значение не равно Nil, Скрытый смысл этого средства состоит в том, что семафор никогда не принадлежит ни одному отдельному потоку. Предположим, что максимальное значение счетчика семафора было равно 10, и десять потоков вызвали функцию WaitForSingleObject(). В результате счетчик потоков сбрасывается до нуля и тем самым семафор переводится в недоступное состояние. После этого достаточно одному из потоков вызвать функцию ReleaseSemaphore() и в качестве параметра lReleaseCount передать число 10, как семафор не просто будет снова пропускать потоки, т.е. станет доступным, но и увеличит значение своего счетчика до прежнего числа — до 10.

Для освобождения дескриптора семафора, выделенного ему с помощью функции CreateSemaphore(), следует вызывать функцию CloseHandle().


События.

События используются в качестве сигналов о завершении какой-либо операции. Однако, в отличие от мьютексов, они не принадлежат ни одному потоку. Например, поток А создает событие с помощью функции CreateEvent и устанавливает объект в состояние «занято». Поток В получает дескриптор этого объекта, вызвав функцию OpenEvent, затем вызывает функцию WaitForSingleObject, чтобы приостановить работу до того момента, когда поток А завершит конкретную задачу и освободит указанный объект. Когда это произойдет, система выведет из состояния ожидания поток В, который теперь владеет информацией, что поток А завершил выполнение своей задачи.



1.2 Синхронизация потоков, классические задачи

Существует достаточно обширный класс средств операционной системы, с помощью которых обеспечивается взаимная синхронизация процессов и потоков. Во многих операционных системах эти средства называются средствами межпроцессорного взаимодействия (Inter Process Communications, IPC).

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

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



Проблема читателей и писателей моделирует доступ к базе данных. Например, БД бронирования билетов на самолет, к которой пытается получить доступ множество процессов. Можно разрешить одновременное считывание данных из базы, но если процесс записывает информацию в базу, доступ остальных процессов должен быть прекращен, даже доступ на чтение. Вопрос: как запрограммировать читателей и писателей?

Постановка задачи «спящего брадобрея». Действие этой проблемной ситуации разворачивается в парикмахерской. В парикмахерской есть один брадобрей, его кресло и n стульев для посетителей. Если желающих воспользоваться его услугами нет, брадобрей сидит в своем кресле и спит. Если в парикмахерскую приходит клиент, он должен разбудить брадобрея. Если клиент приходит и видит, что брадобрей занят, он либо садится на стул (если есть место), либо уходит (если места нет). Необходимо запрограммировать брадобрея и посетителей так, что бы избежать состояния истязания. У этой задачи существует много аналогов в сфере массового обслуживания, например информационная служба, обрабатывающая одновременно ограниченное количество запросов, с компьютеризированной системой ожидания для запросов.

Постановка задачи «обедающие философы». Задача заключается в следующем: пять философов сидят за круглым столом, и у каждого есть тарелка со спагетти. Спагетти скользкие, и поэтому каждому философу нужно две вилки. Между каждыми двумя тарелками лежит одна вилка.

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



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

2. Постановка задачи.


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

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

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

Проблемы


Задача сформулирована таким образом, чтобы иллюстрировать проблему избежания взаимной блокировки (англ. deadlock) — состояния системы, при котором прогресс невозможен.
Например, можно посоветовать каждому философу выполнять следующий алгоритм:


  • Взять либо вилку, либо нож, либо взять оба прибора (если доступны)

  • Есть

  • Положить вилку

  • Положить нож

  • Размышлять

  • Повторить алгоритм сначала

Это решение задачи некорректно: оно позволяет системе достичь состояния взаимной блокировки, когда один философ взял вилку и ждет ножа, а второй взял нож и ждет вилку.


Проблема ресурсного голодания (англ. resource starvation) может возникать независимо от взаимной блокировки, если один из философов не может завладеть ножом и вилкой из-за проблем синхронизации. Например, может быть предложено правило, согласно которому философы должны класть вилку обратно на стол после пятиминутного ожидания доступности ножа, и ждать ещё пять минут перед следующей попыткой завладеть вилками. Эта схема устраняет возможность блокировки (так как система всегда может перейти в другое состояние), но по-прежнему существует возможность «зацикливания» системы (англ. livelock), при котором состояние системы меняется, но она не совершает никакой полезной работы. Например, если все пять философов появятся в столовой одновременно.
Взаимное исключение (англ. mutual exclusion) является основной идеей «Проблемы обедающих философов». Эта проблема представляет собой общий, абстрактный сценарий, позволяющий объяснить проблемы этого типа. Ошибки философов наглядно демонстрируют те трудности, которые возникают в реальном программировании, когда нескольким программам требуется исключительный доступ к разделяемым ресурсам. Эти вопросы изучаются в области параллельных вычислений.

Решение задачи. Иерархия ресурсов.

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


Пусть вилка имеет номер 1, а нож – 2, и каждая рабочая единица (философ) всегда берёт сначала вилку, а потом нож из доступных. Далее, философ кладёт сначала нож, потом — вилку.
Данное решение было предложено Дейкстрой.
В то время, как иерархия ресурсов позволяет избежать взаимных блокировок, данное решение не всегда является практичным, в особенности когда список необходимых ресурсов неизвестен заранее. Например, если рабочая единица удерживает ресурс 3 и 5 и решает, что ей необходим ресурс 2, то она должна выпустить ресурс 5, затем 3, после этого завладеть ресурсом 2 и снова взять ресурс 3 и 5. Компьютерные программы, которые работают с большим количеством записей в базе данных, не смогут работать эффективно, если им потребуется выпускать все записи с верхними индексами прежде, чем завладеть новой записью.

3. Использованные API-функции


В работе использовались следующие API-функции:


  1. CreateSemaphore – создание семафора

function CreateSemaphore (flpSemaphoreAttributes: PSecurityAttributes; llnitialCoimt,iMaximumCount: Longint; IpName: PChar): THandle; stdcall;




  • flpSemaphoreAttributes – указатель на запись TSecurityAttributes, значение Nil соответствует согласию на использование стандартных атрибутов защиты.

  • llnitialCount представляет собой начальное значение счетчика семафорного объекта. Это число может находиться в диапазоне от 0 до значения IMaximumCount. Семафор доступен, если значение этого параметра больше нуля. Когда поток вызывает функцию WaitForSingleObject() любую другую, ей подобную, значение счетчика семафора уменьшается на единицу. И наоборот, при вызове потоком функции ReleaseSemaphore() значение счетчика семафора увеличивается на единицу.

  • IMaximumCount задает максимальное значение счетчика семафорного объекта. Если семафор используется для подсчета некоторых ресурсов, это число должно представлять общее количество доступных ресурсов.

  • IpName содержит имя семафора.




  1. ReleaseSemaphore - увеличения значения счетчика семафора

function ReleaseSemaphore(hSemaphore: THandle; IReleaseCount: Longint; IpPreviousCount: Pointer): BOOL; stdcall;




  • IReleaseCount задаёт число, на которое будет уменьшено значение счетчика семафора. При этом старое значение счетчика будет сохранено в переменной типа Longint, на которую указывает параметр IpPreviousCount, если его значение не равно Nil,




  1. CloseHandle – закрытие дескриптора

function CloseHandle(hObject: THandle): BOOL; stdcall;



function CloseHandle; external kernel32 name 'CloseHandle';
HObject – дескриптор объекта


  1. WaitForSingleObject – приостановка выполнения потока до того момента, как заданный объект перейдет в состояние свободно.

function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD): DWORD; stdcall;

function WaitForSingleObject; external kernel32 name 'WaitForSingleObject';


  • hHandle - дескриптор объекта, уведомление о свободном состоянии которого требуется получить

  • dwMilliseconds - это время, которое вызывающий поток готов ждать. Если dwMilliseconds равно нулю, функция немедленно вернет текущий статус заданного объекта. Таким образом, можно протестировать состояние объекта. Параметру можно также присваивать значение символьной константы INFINITE (= -1), в этом случае вызывающий поток будет ждать неограниченное время.

Функция WaitForSingleObject переводит вызывающий поток в состояние ожидания до того момента, когда она передаст ему свое возвращаемое значение. Возвращаемые значения:

  • wait_object_0 - объект находится в состоянии «свободно»;

  • WAIT_TIMEOUT - интервал ожидания, заданный dwMilliseconds, истек,

а нужный объект по прежнему находится в состоянии «занято»;

  • WAIT_ABANDONED относится только к мьютексу и означает, что объект не

был освобожден потоком, который владел им до своего завершения;

  • WAIT_FAILED - при выполнении функции произошла ошибка




  1. CreateThread – создание потока.

function CreateThread (psa: PSecurityAttributes; cbStackSize:DWORD, pfnStartAddr: PTHREAD_START_ROUTIME, pvParam: PVOID, dwCreateFlags: DWORD, pdwThreadID: PDWORD): THandle; stdcall;




  • psa – Параметр psa является указателем на структуру SECURITY_ATTRIBUTES (аттрибуты защиты)

  • cbStackSize - Этот параметр определяет, какую часть адресного пространства поток сможет использовать под свой стек.

  • pfnStartAddr - определяет адрес функции потока, с которой должен будет начать работу создаваемый поток

  • pvParam – указатель на параметр функции pfnStartAddr.

  • dwCreateFlags - Этот параметр определяет дополнительные флаги, управляющие созданием потока. Он принимает одно из двух значений: 0 (исполнение потока начинается немедленно) или CREATE_SUSPENDED.

  • pdwThreadID - это адрес переменной типа DWORD, в которой функция возвращает идентификатор, приписанный системой новому потоку.

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

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

4. Модульная схема



Интерфейс пользователя и организация работы потоков




Unit1



Модуль для работы с потоками



Filosof


Описание основного модуля Unit1.pas

procedure ShowState(State:Byte;FilosofIndex:Byte) – процедура показывает на форме состояние State философа FilosofIndex

procedure FilosofVilkaCapture(Sender: TObject); - процедуры обработки

procedure FilosofNojCapture(Sender: TObject); взятия/освобождения вилки или ножа

procedure FilosofVilkaPut(Sender: TObject);

procedure FilosofNojPut(Sender: TObject);



Описание модуля Filosof.pas


В данном модуле описан класс TFilosof



5. Результаты тестирования программы

Начальное состояние программы



Состояние после запуска







Заключение

Итак, в этой курсовой работе было реализовано решение задачи «обедающие философы». Были изучены и закреплены навыки работы с потоками (создание, удаление), синхронизация потоков и обработка исключительных ситуаций.

Для разработки приложения использовались функции Win32 API.

Цель этой работы – изучить суть классической проблемы – синхронизации потоков. Несомненно, эта цель была достигнута. Однако мысль не стоит на месте, и возможно, уже были придуманы новые алгоритмы синхронизации, усовершенствованы и созданы новые пути решений. Но основа – суть проблемы – неизменна. Именно основе была посвящена данная курсовая работа.


Использованная литература

1.Э. Таненбаум, Современные операционные системы 2-е изд. - СПб Питер 2002 - 1040 стр.: ил.

2. Назарр К., Рихтер Дж. Windows via C/C++. Изд. Питер, 2009 г. , 896 стр.

3. Русская справка по Windows API, Windows SDK

4. http://www.msdn.microsoft.com

Приложение




Основной модуль Filosof.pas



unit Filosof;
interface

uses Windows,SysUtils,Classes;

type

TFilosof = class



private

FVilka:THandle;

FNoj:THandle;

FThread:THandle;

FState:byte; // 0-Думает 1-Ест 2-Голодает 4 - Взял только вилку
FWorkThread:Boolean;

FTimeToThink:Cardinal; // в секундах

FTimeToEat:Cardinal;
FOnVilkaCapture:TNotifyEvent;

FOnNojCapture:TNotifyEvent;

FOnVilkaPut:TNotifyEvent;

FOnNojPut:TNotifyEvent;


procedure SetTimeToThink(const Value: Cardinal);

procedure SetTimeToEat(const Value: Cardinal);
protected

procedure DoVilkaCapture; virtual;

procedure DoNojCapture; virtual;

procedure DoVilkaPut; virtual;

procedure DoNojPut; virtual;
public

property TimeToThink:Cardinal read FTimeToThink write SetTimeToThink;

property TimeToEat:Cardinal read FTimeToEat write SetTimeToEat;

property State:Byte read FState;
property OnVilkaCapture: TNotifyEvent read FOnVilkaCapture write FOnVilkaCapture;

property OnNojCapture: TNotifyEvent read FOnNojCapture write FOnNojCapture;

property OnVilkaPut: TNotifyEvent read FOnVilkaPut write FOnVilkaPut;

property OnNojPut: TNotifyEvent read FOnNojPut write FOnNojPut;
constructor Create(AVilka,ANoj:THandle);

destructor Destroy; override;

procedure Start;

procedure Stop;

end;
implementation
procedure ThreadWorking(Self:TFilosof); stdcall;

begin

while Self.FWorkThread do

begin

Self.FState:=2;

WaitForSingleObject(Self.FVilka,INFINITE); Self.DoVilkaCapture; Self.FState:=4;

WaitForSingleObject(Self.FNoj,INFINITE); Self.DoNojCapture;


if not Self.FWorkThread then Break;
Self.FState:=1;

Sleep(Self.FTimeToEat*1000);

ReleaseSemaphore(Self.FNoj,1,nil); Self.DoNojPut;

ReleaseSemaphore(Self.FVilka,1,nil); Self.DoVilkaPut;


if not Self.FWorkThread then Break;
Self.FState:=0;

Sleep(self.FTimeToThink*1000+1);



end;

Self.FState:=3;



end;
{ TFilosof }
constructor TFilosof.Create(AVilka, ANoj: THandle);

begin

inherited Create;

self.FVilka:=AVilka;

self.FNoj:=ANoj;
FState:=0;

FTimeToThink:=5;

FTimeToEat:=10;

end;
destructor TFilosof.Destroy;

begin
inherited;

end;
procedure TFilosof.DoNojCapture;

begin

if Assigned(FOnNojCapture) then FOnNojCapture(Self);

end;
procedure TFilosof.DoNojPut;

begin

if Assigned(FOnNojPut) then FOnNojPut(Self);

end;
procedure TFilosof.DoVilkaCapture;

begin

if Assigned(FOnVilkaCapture) then FOnVilkaCapture(Self);

end;
procedure TFilosof.DoVilkaPut;

begin

if Assigned(FOnVilkaPut) then FOnVilkaPut(Self);

end;
procedure TFilosof.SetTimeToEat(const Value: Cardinal);

begin

if Value>3600 then raise Exception.Create('Слишком много придется ждать!');

FTimeToEat := Value;



end;
procedure TFilosof.SetTimeToThink(const Value: Cardinal);

begin

if Value>3600 then raise Exception.Create('Слишком много придется ждать!');
FTimeToThink := Value;

end;
procedure TFilosof.Start;

var lpThreadId:Cardinal;

begin

FWorkThread:=true;

FThread:=CreateThread(nil,0,@ThreadWorking,Self,0,lpThreadId);

end;
procedure TFilosof.Stop;

begin

FWorkThread:=false;



end;
end.
//--------------------------------------------------------------------------------------

unit Unit1



interface
uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls,Filosof, Vcl.ExtCtrls,

JvExExtCtrls, JvImage, Vcl.Imaging.jpeg, Vcl.Imaging.GIFImg;


type

TMain = class(TForm)

JvImage1: TJvImage;

JvImage2: TJvImage;

JvImage3: TJvImage;

JvImage4: TJvImage;

JvImage5: TJvImage;

Label1: TLabel;

Label2: TLabel;

Label3: TLabel;

Label4: TLabel;

Label5: TLabel;

Timer1: TTimer;

Label6: TLabel;

Label7: TLabel;

Image1: TImage;

Image2: TImage;

procedure FormShow(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

private

procedure FilosofVilkaCapture(Sender: TObject);

procedure FilosofNojCapture(Sender: TObject);

procedure FilosofVilkaPut(Sender: TObject);

procedure FilosofNojPut(Sender: TObject);
public

{ Public declarations }



end;
TPointF = record

x,y:Integer;



end;
const

nmax = 100;

nFil = 5;
var

Main: TMain;


VilkaHandle,NojHandle:THandle;

Filosofs:array[1..nmax] of TFilosof;

Images:array[1..nmax] of TImage;

ImgOfState:array[1..nmax] of TImage;

TimeGolodovka:Array[1..nmax] of Cardinal;

PrevStateOfFilosof:array[1..nmax] of Byte;


VilkaCnt,NojCnt:Integer;
tmp,tmp2:integer;

implementation
{$R *.dfm}
uses Unit2;
//------------------------------------------------------------------------------
procedure ShowState(State:Byte;FilosofIndex:Byte);

var Img:TImage;

scCenter,imgCenter,Vektor,stateImgCenter:TPointF;



begin

if State = PrevStateOfFilosof[FilosofIndex] then Exit;
Img:=TImage.Create(Main);

Img.Parent:=Main;

Img.Stretch:=true;

if ImgOfState[FilosofIndex]<> nil then ImgOfState[FilosofIndex].Free;

ImgOfState[FilosofIndex]:=Img;


scCenter.x:=Main.Width div 2;

scCenter.y:=Main.Height div 2;


imgCenter.x:=Images[FilosofIndex].Left+Images[FilosofIndex].Width div 2;

imgCenter.y:=Images[FilosofIndex].Top+Images[FilosofIndex].Height div 2;


Vektor.x:=scCenter.x-imgCenter.x;

Vektor.y:=scCenter.y-imgCenter.y;


case State of

0: // Думает



begin

Img.Left:=imgCenter.x;

Img.Top:=imgCenter.y-140;

If FilosofIndex = 3 then Img.Top:=Img.Top+30;
Img.Left:=Img.Left-Img.Width div 2;

Img.Top:=Img.Top-Img.Height div 2;


Img.Picture.LoadFromFile('./img/380.jpg');

end;

1: // Ест



begin

Img.Picture.LoadFromFile('./img/382.jpg');

stateImgCenter.x:=imgCenter.x+Round(Vektor.x*0.4);

stateImgCenter.y:=imgCenter.y+Round(Vektor.y*0.4);


stateImgCenter.x:=stateImgCenter.x-Img.Width div 2;

stateImgCenter.y:=stateImgCenter.y-Img.Height div 2;


Img.Left:=stateImgCenter.x;

Img.Top:=stateImgCenter.y;



end;

2: // Голодает



begin

Img.Left:=imgCenter.x;

Img.Top:=imgCenter.y-140;

If FilosofIndex = 3 then Img.Top:=Img.Top+30;
Img.Left:=Img.Left-Img.Width div 2;

Img.Top:=Img.Top-Img.Height div 2;


Img.Stretch:=false;

Img.AutoSize:=true;

Img.Picture.LoadFromFile('./img/381.jpg');

end;

3: // Резерв



begin

Img.Free;

ImgOfState[FilosofIndex]:=nil;

end;
4:

begin

Img.Left:=imgCenter.x;

Img.Top:=imgCenter.y-140;

If FilosofIndex = 3 then Img.Top:=Img.Top+30;
Img.Left:=Img.Left-Img.Width div 2;

Img.Top:=Img.Top-Img.Height div 2;


Img.Stretch:=false;

Img.AutoSize:=true;

Img.Picture.LoadFromFile('./img/383.jpg');

end;

end;
PrevStateOfFilosof[FilosofIndex]:=State;

end;
//------------------------------------------------------------------------------
procedure TMain.FilosofNojCapture(Sender: TObject);

begin

InterlockedDecrement(NojCnt);



end;
procedure TMain.FilosofNojPut(Sender: TObject);

begin

InterlockedIncrement(NojCnt);



end;
procedure TMain.FilosofVilkaCapture(Sender: TObject);

begin

InterlockedDecrement(VilkaCnt);



end;
procedure TMain.FilosofVilkaPut(Sender: TObject);

begin

InterlockedIncrement(VilkaCnt);



end;
procedure TMain.FormShow(Sender: TObject);

var i:integer;

begin

VilkaHandle:=CreateSemaphore(nil,0,nmax,nil);

NojHandle:=CreateSemaphore(nil,0,nmax,nil);
for i:=1 to nFil do

begin

Filosofs[i]:=TFilosof.Create(VilkaHandle,NojHandle);

Filosofs[i].OnVilkaCapture:=FilosofVilkaCapture;

Filosofs[i].OnNojCapture:=FilosofNojCapture;

Filosofs[i].OnVilkaPut:=FilosofVilkaPut;

Filosofs[i].OnNojPut:=FilosofNojPut;



end;

Images[1]:=JvImage2;

Images[2]:=JvImage1;

Images[3]:=JvImage3;

Images[4]:=JvImage4;

Images[5]:=JvImage5;



end;

procedure TMain.Timer1Timer(Sender: TObject);

var i:integer;

begin

Form2.Left:=Main.Left+Main.Width+10;

Form2.Top:=Main.Top+10;

if not Form2.Visible then begin Form2.Show; Main.SetFocus; end;
for i:=1 to nFil do

ShowState(Filosofs[i].State,i);


Label6.Caption:=IntTOStr(VilkaCnt);

Label7.Caption:=IntTOStr(NojCnt);



end;
end.
//-------------------------------------------------------------------------------------

unit Unit2



interface
uses

Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,

Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, JvExStdCtrls, JvGroupBox,

Vcl.Samples.Spin, Vcl.ExtCtrls;


type

TForm2 = class(TForm)

JvGroupBox1: TJvGroupBox;

JvGroupBox2: TJvGroupBox;

JvGroupBox3: TJvGroupBox;

JvGroupBox4: TJvGroupBox;

JvGroupBox5: TJvGroupBox;

SpinEdit1: TSpinEdit;

Label1: TLabel;

SpinEdit2: TSpinEdit;

Label2: TLabel;

SpinEdit3: TSpinEdit;

SpinEdit4: TSpinEdit;

Label3: TLabel;

Label4: TLabel;

SpinEdit5: TSpinEdit;

SpinEdit6: TSpinEdit;

Label5: TLabel;

Label6: TLabel;

SpinEdit7: TSpinEdit;

SpinEdit8: TSpinEdit;

Label7: TLabel;

Label8: TLabel;

SpinEdit9: TSpinEdit;

SpinEdit10: TSpinEdit;

Label9: TLabel;

Label10: TLabel;

Timer1: TTimer;

Label11: TLabel;

Label12: TLabel;

Label13: TLabel;

Label14: TLabel;

Label15: TLabel;

Label16: TLabel;

Label17: TLabel;

Label18: TLabel;

Label19: TLabel;

Label20: TLabel;

JvGroupBox6: TJvGroupBox;

Button1: TButton;

Button2: TButton;

Button3: TButton;

Button4: TButton;

SpinEdit11: TSpinEdit;

Label21: TLabel;

SpinEdit12: TSpinEdit;

Label22: TLabel;

procedure SpinEdit1Change(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure Button1Click(Sender: TObject);

procedure Button2Click(Sender: TObject);

procedure Button4Click(Sender: TObject);

procedure Button3Click(Sender: TObject);

private

{ Private declarations }



public

{ Public declarations }



end;
var

Form2: TForm2;


implementation
{$R *.dfm}
uses Unit1;
procedure TForm2.Button1Click(Sender: TObject);

var i:integer;

begin

while WaitForSingleObject(Unit1.VilkaHandle,1)<>WAIT_TIMEOUT do ; // Убираем все вилки и ножи со стола

while WaitForSingleObject(Unit1.NojHandle,1)<>WAIT_TIMEOUT do ;
for i:=1 to nFil do TimeGolodovka[i]:=0;
VilkaCnt:=SpinEdit11.Value;

NojCnt:=SpinEdit12.Value;


ReleaseSemaphore(Unit1.VilkaHandle,SpinEdit11.Value,nil); // Кладем чистые вилки и ножи

ReleaseSemaphore(Unit1.NojHandle,SpinEdit12.Value,nil);


for i:=1 to nFil do Filosofs[i].Start;

end;
procedure TForm2.Button2Click(Sender: TObject);

var i:integer;

begin

for i:=1 to nFil do Filosofs[i].Stop;

end;
procedure TForm2.Button3Click(Sender: TObject);

begin

Application.MessageBox('Курсовая работа по Операционным Системам' + #13#10 +

'на тему:' + #13#10#13#10 + '"Разработка ПО для решения задачи ' +

#13#10 + '"обедающих философов" с использованием ' + #13#10 +

'синхронизации с помощью семафоров"' + #13#10#13#10 +

'Выполнил: ст. гр. ПВ-32' + #13#10 + 'Юсюмбели Н. И',

'Информация о программе', MB_OK + MB_ICONINFORMATION + MB_TOPMOST);
end;
procedure TForm2.Button4Click(Sender: TObject);

begin

Application.Terminate;



end;
procedure TForm2.SpinEdit1Change(Sender: TObject);

var k:integer;

begin

k:=(Sender as TComponent).Tag;



if k>10 then

begin

k:=k-10;


Unit1.Filosofs[k].TimeToEat:=(Sender as TSpinEdit).Value;

end else Unit1.Filosofs[k].TimeToThink:=(Sender as TSpinEdit).Value;

end;
procedure TForm2.Timer1Timer(Sender: TObject);

var i,sum:integer;

begin

SpinEdit1.Value:=Unit1.Filosofs[1].TimeToThink;

SpinEdit3.Value:=Unit1.Filosofs[2].TimeToThink;

SpinEdit5.Value:=Unit1.Filosofs[3].TimeToThink;

SpinEdit7.Value:=Unit1.Filosofs[4].TimeToThink;

SpinEdit9.Value:=Unit1.Filosofs[5].TimeToThink;


SpinEdit2.Value:=Unit1.Filosofs[1].TimeToEat;

SpinEdit4.Value:=Unit1.Filosofs[2].TimeToEat;

SpinEdit6.Value:=Unit1.Filosofs[3].TimeToEat;

SpinEdit8.Value:=Unit1.Filosofs[4].TimeToEat;

SpinEdit10.Value:=Unit1.Filosofs[5].TimeToEat;
for i:=1 to nFil do

if Unit1.Filosofs[i].State = 2 then inc(Unit1.TimeGolodovka[i]);
sum:=0;

for i:=1 to nFil do

sum:=sum+Unit1.TimeGolodovka[i];



if sum=0 then sum:=1;
Label12.Caption:=Format('%d [%.0f%%] сек.',[Unit1.TimeGolodovka[1],Unit1.TimeGolodovka[1]/sum*100]);

Label14.Caption:=Format('%d [%.0f%%] сек.',[Unit1.TimeGolodovka[2],Unit1.TimeGolodovka[2]/sum*100]);

Label16.Caption:=Format('%d [%.0f%%] сек.',[Unit1.TimeGolodovka[3],Unit1.TimeGolodovka[3]/sum*100]);

Label18.Caption:=Format('%d [%.0f%%] сек.',[Unit1.TimeGolodovka[4],Unit1.TimeGolodovka[4]/sum*100]);



Label20.Caption:=Format('%d [%.0f%%] сек.',[Unit1.TimeGolodovka[5],Unit1.TimeGolodovka[5]/sum*100]);

end;
end.



Смотрите также:
Курсовая работа по товароведению продовольственных товаров на тему: сигары
298.27kb.
1 стр.
Курсовая работа По дисциплине "Введение в специальность" На тему: "Производство стали"
307.13kb.
1 стр.
Курсовая работа по банковскому делу на тему "Банковские пластиковые карточки"
309.03kb.
1 стр.
Курсовая работа на тему "Финансовый рынок"
351.42kb.
1 стр.
Курсовая работа по дисциплине : "Деньги, кредит, банки" на тему
554.13kb.
2 стр.
Курсовая работа цели курсовой работы
110.1kb.
1 стр.
Курсовая работа на тему
473.95kb.
1 стр.
Курсовая работа на свободную тему «развитие осознанности и интуиции. Телесно-ориентированная психотерапия по имени айкидо»
139.18kb.
1 стр.
Курсовая работа по курсу «Программирование» Тема работы: «Разработка приложения с графическим интерфейсом на языке C++ с использованием библиотеки qt»
301.1kb.
1 стр.
Объекты морской техники
135.16kb.
1 стр.
Курсовая работа на тему: Экономические законы и экономические категории
554.96kb.
5 стр.
Курсовая работа по предмету Информатика на тему
62.27kb.
1 стр.