Модераторы: Snowy, MetalFan, bems, Poseidon
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Диспетчеризация процессов с приоритетами по времен, Создать очередь с учётом приоритетов 
V
    Опции темы
AnReykfi
  Дата 7.12.2009, 21:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 7.12.2009

Репутация: нет
Всего: нет



Здравствуйте!
У меня вот выявилась проблема: Надо сдавать КП, а я ни как не могу написать программу.

Суть программы:
Написать на Pascal или Delphi (другого не знаю) следующее:
Диспетчеризация процессов (очередей или заявок) с учётом относительных приоритетов.

Нам дано:
1-ая Очередь из которой нам приходят "процессы". Фиксированного размера(размер=сколько заявок нужно обработать, мы вводим в ручную кол-во заявок) Заранее не известно какой "процесс" придёт.
Очередь забивается заранее в ручную в  стек на подобие массива, или связанных списков, или организации стека-ДЕК на выбор. 
Каждый элемент в массиве описывается следующими параметрами как:
- Номер процесса
- Время прихода к процессору
- Длительность выполнения "процесса"
- и самое важное: ПРИОРИТЕТ данного "процесса".
2-ая очередь: Динамически изменяющаяся, зависит от кол-ва элементов помещенных в неё. Можно сортировать. Типа "динамически-отсортированного буфера".

По видимому как я понимаю должно быть как на картинке:

user posted image

Теперь суть:
Забиваем наш типа "СТЕК", из эН-элементов сколько мы захотим, это будет Очередь 1:
Приходит заявка из 1ой очереди, если процессор не занятый, и нет заявок во 2ой очереди (либо она пуста), то заявка начинает выполняться процессором столько времени, сколько ей нужно.
Тута начинается цикл:
Приходит вторая заявка, если заявка Номер 1 еще не обслужилась процессором, то Заявка Номер 2 становиться в Очередь 2, если обслужилась то начинает выполнение вторая заявка.
Приходит 3тья заявка, а Первая еще обслуживается,  то она становиться  в очередь 2.
Но вот случилось "чудо", Первая заявка облужилась, но по времени прихода пришла Четвертая заявка, теперь начинается "разбор полетов" между 4той-которая только что пришла, 3тье и 2ой-которые уже были на приеме у процессора, но не были выполнены. Из Очереди 2 Ищется Заявка с наивысшим приоритетом(например "0")(как я поняла метотодом банальной сортировки) и сравнивается с новенькой, если кто-то из Очереди 2 выше приоритетом "Новенькой" 4той из Очереди 1, то заявка из Очереди 2 становиться на выполнение, а 4тая заявка становиться в Очередь 2 и ждёт своего часа; если же "Новенькая" выше приоритетом заявок из Очереди 2 то она становиться на выполнение, а остальные в очереди 2 ждут своего часа.  

Но вот пришла проблема, наш "СТЕК" а именно Очередь 1 стала пустой, а в Очереди 2 есть еще заявки, тогда мы их сортируем и "выполняем" по приоритетам.

Организовать вывод на экран (или диалоговое окно):
- возможность ручного заполнения СТЕКА-Очереди 1 нужным нам количеством  заявок...
- вывести состояние очереди 1 до выполнения диспетчеризации...
- выполнить диспетчеризацию...
- выводить через каждый такт состояние 1 очереди(какая типа заявка пришла), процессора (какая заявка щас выполняется и сколько тактов ей осталось), состояние Очереди 2 ( какие заявки стоят и в каком виде)...


Итд... с тем количеством заявок которые мы указали......
Огранизовать это:
- "СТЕК"
- эти циклы
- дружественный интерфейс Можно в Виндовсном окне или в ДОСе.... ну главное что бы мне было понятно.

Ну вот и всё=))) Заранее спасибо=))
ЗЫ. Пыталась всё конкретно разжевать то что мне нужно.
ЗЗЫ. тип данных любой.

 

Это сообщение отредактировал(а) AnReykfi - 15.5.2010, 12:42
PM MAIL   Вверх
Демо
Дата 7.12.2009, 22:08 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Цитата(AnReykfi @  7.12.2009,  21:34 Найти цитируемый пост)
2-ая очередь: Динамически изменяющаяся, зависит от кол-ва элементов помещенных в неё. Можно сортировать. Типа "динамически-отсортированного буфера".


Эта очередь определена по условию, или выдумана?

Не определены рамки понятия "Приоритет" - от него зависит количество "очередей".

Цитата(AnReykfi @  7.12.2009,  21:34 Найти цитируемый пост)
- выводить через каждый такт


Не определено понятие "такт". Либо это квант времени зависящий от заявки, либо от диспетчера.






--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 7.12.2009, 23:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



В 2006 г. была подобная задача. "Очередь без приоритетов" - название.

Ещё дополнительный вопрос к заданию - можно ли пользоваться готовыми классами, которые реализованы в Delphi?
Это не очень принципиально, но уростит реализацию.

И ещё вопрос - что вызывает затруднения?


--------------------
    
PM MAIL ICQ Skype   Вверх
AnReykfi
Дата 8.12.2009, 00:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 7.12.2009

Репутация: нет
Всего: нет



Цитата

Эта очередь определена по условию, или выдумана?


СТОП, опредена следующими условиями:
Если в ней ничего нет, то она пустая...
Если в неё что-то собираемся ложить, то добавляется 1 элемент (Номер процесса, его приоритет и время выполнения)
Если что-то еще приходит то Очередь "расширяется" еще на 1 и туда кладётся то что пришло.... а потом сортируется по приоритетам..
Если что-то из очереди 2 ставиться на выполнение, то на уменьшается на 1 элемент....
Если что-то в очередь кладется (т.е Пришел из Очереди 1 низкоприоритетная заявка(или заявка котрая вызвала тупик- застопоривание(Заявка из очереди 1 по приоритету = Завке из Очереди 2 ), а в Очереди 2 есть более приоритетная заявка, которая пошла только что в этот такт времени на выполнение), то очередь 1 не меняет своего размера. (кажись так...) 
 

Вот мои первые шаги в развитии данного вопроса.... коряво, знаю....

Код

program PRIORITET;
uses crt;
label
  L2; {сделана для будующим}
var
  i, j: integer;
  x : integer ;
  q,w,e,r,y,T:integer;
  OtP : array [0..100, 0..100] of integer; {Первая очередь в виде массива которую я определила что она будет под стек}

BEGIN

clrscr;
textcolor(red);
textbackground(black);
Writeln('Vvedite kolitschestvo prozessov: ');
readln(x);
Writeln('Kolitschestvo prozessov vibrano: ', x );
Writeln('Zabivaem massiv:');
Writeln('     Nomer proz|Kogda prishel|PRIO|Vremya vip');
for i:=1 to 4 do                {zabivanie massiva}
           begin
           write('Prozessi N ',i , '  '); { будет ввод таким образом: сначала забиваем номера процессов(типа это будет их имена) через пробел потом жмем Энтер
Потом забиваем время прихода черерез пробел у каждого Энтер жмем
Потом забиваем их Приоритет через пробел и жмем Энтер
Потом забиваем время выполнения
}
          for j:=1 to x do

             read (OtP[i,j]);
             readln;
             end;

Writeln('___ ');

for i:=1 to 4 do    {vivod tekuschego zabitogo massiva}
          begin
            for j := 1 to x  do  write(OtP[i,j], ' '); writeln;
          end ;

readln;


 {na4alo dipet4erizacii}
for q:=1 to x do   {skolko taktov u nas budet}
  Begin
   T:= OtP[4,q] + T;
   end;
writeln (T);

readln;

END.


И вот типа вывод на Экран:
user posted image

Такт, как бы это сказать..... щас попробую нарисую табличку....



Сама очередь:

Такт - типа взятие из массива элемента и его обработка(постановка на выполнение и само выполнение, либо установка в очередь 2)

Номер процесса   | 1 | 2 | 3 | 4 | 5 | 6 | ..... вводим в ручную
_________________________________________

Когда пришел       | 1 | 2 | 4 | 5 | 7 | 9 |..... в какой такт пришел на обработку
_________________________________________

Приоритет             | 2 | 1 | 3 | 0 | 1 | 2 |.... 0 - самый высокий приоритет
_________________________________________

Время выполнения | 2 | 3 | 4 | 1 | 2 | 4 |.... сколько нужно тактов  или "квантов времени на выполнении данного эН-ного процесса...
_________________________________________


Теперь что должно в конце получиться:

Такт   |  Процессор |  Очередь 2 (то что ждет на выполнение)
--------------------------------------------------------------------------------
1        |     1 (3)        | пусто
2        |     1 (2)        | 2(1прио)
3        |     1 (1)        | 2(1прио)
4        |     2 (2)        | 3(3прио)
5        |     2 (1)        | 4(0прио), 3(3прио)
6        |     4 (1)        | 3(3прио)
7        |     5 (2)        | 3(3прио)
8        |     5 (1)        | 3(3прио) 
9        |     6 (4)        | 3(3прио)
10      |     6 (3)        | 3(3прио)
11      |     6 (2)        | 3(3прио)
12      |     6 (1)        | 3(3прио)
13      |     3 (4)        | пусто
14      |     3 (3)        | пусто
15      |     3 (2)        | пусто
16      |     3 (1)        | пусто
17      |   пусто        | пусто 

Колличесво тактов для выполнения например этой задачки = 16, просто суммируем изначально ВСЁ время, которое требуется для выполнения ВСЕХ заявок. Т.е: 3 + 2 + 4 + 1 + 2 + 4 = 16, а 17тый нужен для сигнала окончания "моделирования".

+ я еще заметила одну закономерность, если так это можно сказать: если в Очереди1 и в Очереди 2 есть заявки с одинаковым приоритетом, то пусть выполняется та заявка, которая находиться в Очереди2, т. е На выполнения из Очереди1 пришла заявка с Приоритетом 1 , а в очереди 2 самая Приоритетная тоже имеет приоритет 1, то тогда Только что пришедшая заноситься в Очередь 2, а на выполнение встаёт та заявка, которая была из Очереди 2. Таким образом можно решить проблему "тупика". 

Ну вот....

Добавлено @ 00:40
Цитата

Ещё дополнительный вопрос к заданию - можно ли пользоваться готовыми классами, которые реализованы в Delphi?
Это не очень принципиально, но уростит реализацию.


Да чем угодно(Delphi, Pascal - библиотеки)! Главное что бы реализовался данный алгоритм.

Цитата

И ещё вопрос - что вызывает затруднения?


Ну ка бы это сказать... вызывает затруднение много чего, не могу правильно реализовать этот "СТЕК", тот который у меня условно называется Очередь 1, потом логика забивается у меня на втором "if", не могу правильно по этому посторить программу, что за чем и в какой последовательности должно следовать..... 
+ Из-за неопытности (учила Паскаль на 1ом курсе вечернего отделения и делали мы всего 6 лабораторок довольно простых, а этого маало, чертовски мало по сравнению с дневным отделением у которых было 27 лабораторок в сесместре + курсач) .... потом с периода конца первого курса по начало 5того мы больше Паскалем не занимались...... занимались каждый курс новым языком... MCL, Prolog, Assembler, MATLAB, GPSS... как говориться галопом по европам, а более углубленно Паскалем и другим языками мне не не удавалось заниматься, работа, семья, дети... То что требоваволось в институте, всё исправно выполняла... а тут задали ЭТО!.. я в шоке....




Это сообщение отредактировал(а) AnReykfi - 8.12.2009, 00:55
PM MAIL   Вверх
Демо
Дата 8.12.2009, 00:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



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

Добавлено через 40 секунд
Кстати, почему консоль?
ТОлько из-за неопытности, или требование такое - без графического интерфейса?


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 01:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



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

1. Структура для определения и хранения информации о задании:


Код

  TStatusJob=(sjWait,sjActive,sjComplete);

  PJob=^TJob;                    
  TJob=record
    Id: Integer;                           //Идентификатор
    Priority: Integer;                   //Приоритет 
    FullTime: Integer;                  //ВРемя, необюходимое для выполнения задания  
    StartTime: TDateTime;          //Время помещения задания в очередь
    TimeActive: Integer;             //Время активности задания
    TimeWait: Integer;               //Время простоя (ожидания выполнения)
    Status: TStatusJob;              //Состояние задания
  end;



Добавлено @ 01:32
2. Для обработки заданий, добавления будем использовать класс TProcessor
Очередь будем хранить в TList.
Код

  TProcessor=class
  private
    FUniqueId: Integer;
    FKvant: Integer;
    FListJob: TList;
    FProcessorNumber: Integer;
    procedure ScanQueue;
  public
    constructor Create(TimeKvant: Integer=1);
    destructor Destroy; override;

    function AddJob: Integer; virtual;

    property NumberOfProcessor: Integer read FProcessorNumber
      write FProcessorNumber;
  end;



Это начало реализации класса

Добавлено @ 01:33
Здесь 
Код

   FUniqueId: Integer; //Счётчик для идентификаторов
    FKvant: Integer;     //Размер кванта времени (в секундах)
    FListJob: TList;        //Очередь заданий
    FProcessorNumber: Integer;       //Количество процессоров (на всякий случай
    procedure ScanQueue;               //ПРоцедура для периодического сканирования очереди


Добавлено @ 01:35
Код

    constructor Create(TimeKvant: Integer=1);
    destructor Destroy; override;

    function AddJob: Integer; virtual;

    property NumberOfProcessor: Integer read FProcessorNumber
      write FProcessorNumber;



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

AddJob - добавление задания в очередь.

Добавлено @ 01:37
Функция AddJob (заполнение структуры задания и добавление в очередь.
Возвращает идентификатор добавленного задания (вдруг понадобтся).
В классе определена с модификатором virtual для того, чтобы при необходимости её можно было в наследниках переопределить.
Код

function TProcessor.AddJob: Integer;
var
  p: PJob;
begin
  New(p);
  Inc(FUniqueId);
  p^.Id := FUniqueId;
  p^.Priority := Random(9)+1;
  p^.FullTime := Random(50)+10;
  p^.StartTime := Now;
  p^.TimeActive := 0;
  p^.TimeWait := 0;
  p^.Status := sjWait;
  Result := p^.Id;
  FListJob.Add(p);
end;



Добавлено @ 01:37
Конструктор и деструктор:

Код

constructor TProcessor.Create(TimeKvant: Integer);
begin
  FKvant := TImeKvant;
  FListJob := TList.Create;
  FProcessorNumber := 1;
end;

destructor TProcessor.Destroy;
begin
  while FListJob.Count>0 do
  begin
    Dispose(PJob(FListJob[0]));
    FListJob.Delete(0);
  end;
  FListJob.Free;
  inherited;
end;



Добавлено через 11 минут и 39 секунд
Отображать результаты работы процессора предлагаю в таком виде:

Это сообщение отредактировал(а) Демо - 8.12.2009, 01:38

Присоединённый файл ( Кол-во скачиваний: 10 )
Присоединённый файл  MainForm.JPG 41,22 Kb


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 01:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



В левой части - TListView, в кеотором будут отображаться все задания во время выполнения (вся очередь)
В правой части - протокол работы процессора - Это либо TListBox, либо TMemo. 

Это сообщение отредактировал(а) Демо - 8.12.2009, 01:45


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 07:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



В общем, немного всё поменялось.

Основное отличие - для каждого приоритета используется своя очередь.
Это упрощает работу с очередями.

Код

//Атрибуты задания
  PJob=^TJob;
  TJob=record
    Id: Integer;              //Идентификатор задания
    Priority: Integer;        //Приоритет
    FullTime: Integer;        //Общее количество квантов
    StartTime: TDateTime;     //Нужно ли время старта?
    KvantActive: Integer;     //Количество выделенных квантов
    KvantWait: Integer;       //Количество квантов простоя
    KvantLast: Integer;       //Осталось квантов
    Status: TStatusJob;       //Статус задания.
  end;


Добавлено @ 07:53
Для работы с очередью немного не подходит стандартный класс в Delphi (TQueue), так как он не даёт доступа к отдельным элементам очереди.
Реализуем простой подобный класс.

Код

//Класс - очередь.
//Стандартный TQueue не подходит, так как в нём
//нет доступа непосредственно к элементам очереди
  TAppQueue=class
  private
    FList: TList;
    function GetCount: Integer;
    function GetItem(Index: Integer): PJob;
  public
    constructor Create;
    destructor Destroy; override;
    function Peek: PJob;
    function Pop: PJob;
    procedure Push(Job: PJob);

    property Job[Index: Integer]: PJob read GetItem;
    property Count: Integer read GetCount;
  end;




Код

{ TAppQueue }

constructor TAppQueue.Create;
begin
//Для хранения элементов будем использовать
//стандартный контейнер - TList
  FList := TList.Create;
end;

destructor TAppQueue.Destroy;
begin
//При уничтожении очереди надо освободить память из под
//элементов очереди
  while Count>0 do Dispose(Pop);
  FList.Free;
  inherited;
end;

//Количество элементов в очереди
function TAppQueue.GetCount: Integer;
begin
  Result := FList.Count;
end;

//Возврат элемента из очереди по индексу
function TAppQueue.GetItem(Index: Integer): PJob;
begin
  Result := FList.Items[Index];
end;

//Получение первого в очереди элемента
//без удаления из очереди
function TAppQueue.Peek: PJob;
begin
  if Count>0
    then Result := FList[0]
    else Result := nil;
end;

//Получение первого в очереди элемента
//с удалением из очереди
function TAppQueue.Pop: PJob;
begin
  Result := Peek;
  if Count>0 then FList.Delete(0);
end;

//Добавление элемента в очередь
procedure TAppQueue.Push(Job: PJob);
begin
  FList.Add(Job);
end;

end.


Добавлено через 9 минут и 22 секунды
Для хранения всех задания будем использовать массив очередей.
В каждом элементе массива будет храниться очередь заданий с одинаковыми приоритетами.

Код

  TQueueJobs=array of TAppQueue;


Добавлено через 10 минут и 27 секунд
Также определим Callback-функции для обработки протокола и изменения статуса заданий:

Код

//Callback-функции для обратной связи
  TProcessorEvent=procedure(Sender: TObject; Msg: String; var Stop: Boolean) of Object;
  TJobState=procedure(Sender: TObject; Job: PJob; Jobs: TQueueJobs) of Object;



Это сообщение отредактировал(а) Демо - 8.12.2009, 07:57


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 08:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Далее реализуем основной класс - Процессор.
В этом классе предусмотрим следующие функции:

1. Возможность создания очереди с различным количеством приоритетов
2. Добавление заданий в очередь двумя способами:
  - Пользователь может задать некоторые параметры сам
  - Пользователь генерирует задание не задавая параметров. 
    В этом случае  некоторые атрибуты выбираются случайным образом
3. Возможность остановки и запуска обработки очередей в любое время
4. Обработка очереди не должна приводить к зависанию интерфейса пользователя
5. Пользователь может динамически поменять длину кванта в пределах от 1 до 10 секунд.


Для того, чтобы не блокировать интерфейс и реализовать задачу периодического вызова функции или процедуры для отсчёта квантов времени, нам идеально подойдёт стандартный реализованный в Delphi класс - TTimer.
Добавлено @ 08:07
Код

//Основной класс - процессор
  TProcessor=class
  private
    FKvantCounter: Integer;       //Счётчик квантов с момента старта
    FUniqueId: Integer;           //Ид для присвоения заданиям
    FKvant: Integer;              //Длина кванта (в секундах)
    FMaxPriority: Integer;        //Максимальный приоритет (0..Max)
    FJobs: TQueueJobs;
    FInJob: TAppQueue;            //Временная очередь. Сюда помещаются
                                  //задания при добавлении
    FTimer: TTimer;               //Таймер для отсчёта квантов времени
                                  //Текущее выполняющееся задание
    FLastJob: PJob;

//Для вызова пользовательской функции - протоколирование
    FProcessorEvent: TProcessorEvent;
//Для вызова пользовательской функции - изменени статуса задания
    FJobState: TJobState;
//Обработка очереди по таймеру
    procedure ProcessJob(Sender: TObject);
//Изменение атрибутов всех неактивных заданий
    procedure ChangeQueueJobs;
//Вызов пользовательской TProcessorEvent
    procedure ProcessorEvent(const Msg: String);
//Вызов пользовательской TJobState
    procedure JobState(Job: PJob);
//Изменить квант времени
    procedure SetKvant(const Value: Integer);
  public
//По-умолчанию: Максимальный приоритет - 7 (количество - 8), Квант - 1 сек.
    constructor Create(MaxPriority: Integer=7; TimeKvant: Integer=1);
    destructor Destroy; override;
//Автоматическое формирование задания со случайными атрибутами
    function GenerateJob: Integer;
//Добавление задания с заданными атрибутами
    function AddJob(Priority, FullTime: Integer): Integer;
//Запуск процессора
    procedure Start;
//Остановка процессора
    procedure Stop;
//Возврат общего количества заданий в очереди
    function JobsCount: Integer; overload;
//Возврат количества заданий с заданным приоритетом
    function JobsCount(Priority: Integer): Integer; overload;
//Текстовый смысл статуса задания
    function GetStatusStr(Status: TStatusJob): String;
//Поиск задания по идентификатору
    function GetJobById(Id: Integer): PJob;

    property OnEvent: TProcessorEvent read FProcessorEvent
      write FProcessorEvent;
    property OnJobState: TJobState read FJobState
      write FJobState;
    property Kvant: Integer read FKvant write SetKvant;

  end;



Добавлено @ 08:12
Код

constructor TProcessor.Create(MaxPriority: Integer;TimeKvant: Integer);
var
  i: Integer;
begin
//Проинициализируем генератор псевдослучайных чисел
  Randomize;
  FKvant := TimeKvant;
  FKvantCounter := 0;
  FMaxPriority := MaxPriority;
  SetLength(FJobs, MaxPriority+1); //Всего - MaxPriority+1 приоритет (0 - начальный индекс)
  for i := Low(FJobs) to High(FJobs) do FJobs[i] := TAppQueue.Create;
  FInJob := TAppQueue.Create;
  FTimer := TTimer.Create(nil);
  FTimer.Interval := FKvant*1000;
  FTImer.OnTimer := ProcessJob;
  FTImer.Enabled := False;
end;


destructor TProcessor.Destroy;
var
  i: Integer;
begin
  FTimer.Free;
  for i := Low(FJobs) to High(FJobs) do FJobs[i].Free;
  FInJob.Free;
  inherited;
end;


Добавлено @ 08:15
Код

//Добавление задания с заданными атрибутами
function TProcessor.AddJob(Priority, FullTime: Integer): Integer;
var
  p: PJob;
begin
  New(p);
  Inc(FUniqueId);
  p^.Id := FUniqueId;
  p^.Priority := Priority;
  p^.FullTime := FullTime;
  p^.StartTime := Now;
  p^.KvantActive := 0;
  p^.KvantWait := 0;
  p^.KvantLast := p^.FullTime;
  p^.Status := sjWait;
  FInJob.Push(p);
  JobState(p);

  Result := p^.Id;
end;

//Автоматическое формирование задания со случайными атрибутами
function TProcessor.GenerateJob: Integer;
begin
//5-15 квантов для задания
  Result := AddJob(Random(FMaxPriority+1),Random(15)+5);
end;



Это сообщение отредактировал(а) Демо - 8.12.2009, 08:19


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 08:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Остановка и запуск процессора в любой момент:

Код

//Запуск процессора
procedure TProcessor.Start;
begin
  FTImer.Enabled := True;
end;

//Остановка процессора
procedure TProcessor.Stop;
begin
  FTimer.Enabled := False;
end;




--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 08:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Код

//Основная процедура обработки очереди по таймеру
procedure TProcessor.ProcessJob(Sender: TObject);
var
  i: Integer;
  s: String;
  Job: PJob;
begin
//Увеличение счётчика квантов
    Inc(FKvantCounter);
//Разбираем очередь новых поступивших заданий
    while FInJob.Count>0 do
    begin
      Job := FInJob.Pop;
//Если задание в очереди по параметрам не подходит,
//(как такое может быть - непонятно, но ладно)
//Сообщаем об ошибочном задании и удаляем его из очереди
//иначе добавляем в соответствующую очередь
      if (Job^.Priority<=FMaxPriority) and (Job^.Priority>=0)  then
        FJobs[Job^.Priority].Push(Job)
      else
      begin
//Сообщаем пользователю в протокол об ошибке
        ProcessorEvent('В очереди оказалось ошибочное задание. Удалено');
        Dispose(Job);
      end;
    end;

//Проверяем последовательно очереди, начиная с очереди заданий
//с самым высоким приоритетом
    for i := High(FJobs) downto Low(FJobs) do
    begin
      if FJobs[i].Count=0 then Continue; //В этой очереди нет заданий
      FLastJob := FJobs[i].Pop;          //Извлекаем из очереди задание
      FLastJob^.Status := sjActive;      //Устанавливаем заданию признак Активный
      Inc(FLastJob^.KvantActive);        //Число активных квантов увеличивается
      Dec(FLastJob^.KvantLast);          //Уменьшаем число оставшихся квантов

      ChangeQueueJobs;                   //Все задания в очередях изменяем

//Здесь формируем какую угодно информацию для показа в протоколе работы
//Сейчас формируется строка такого вида:
//№ такта: Id задания: приоритет: количество заданий в очереди этого приоритета: всего заданий
      s := IntToStr(FKvantCounter)+' : '+
           IntToStr(FLastJob^.Id)+' : '+
           IntToStr(JobsCount(FLastJob^.Priority)+1)+' : '+
           IntToStr(JobsCount+1)+' : ';
      JobState(FLastJob);                //Сообщаем пользователю об изменениях
      ProcessorEvent(s);                 //Передаём пользователю
                                         //сформированную строку
//Сообщаем о выполняемом задании
//Изменяем состояние всех заданий
      if FLastJob^.KvantLast=0 then
      begin
      //Сообщаем об окончании задания и удаляем из очереди
        FLastJob^.Status := sjComplete;
        JobState(FLastJob);
        Dispose(FLastJob);
      end
      else
      begin
        FJobs[i].Push(FLastJob);        //Задание ещё не закончилось,
                                        //помещаем в конец соответствующей очереди
        FLastJob^.Status := sjWait;     //После окончания кванта задание снова будет ждать
      end;

      Break;
    end;
end;



Это сообщение отредактировал(а) Демо - 8.12.2009, 10:33


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 09:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



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


Одна функция предназначена для ведения протокола, вторая вызывается при каждой смене кванта времени процессора.
Код

//Callback-функции для обратной связи
  TProcessorEvent=procedure(Sender: TObject; Msg: String; var Stop: Boolean) of Object;
  TJobState=procedure(Sender: TObject; Job: PJob; Jobs: TQueueJobs) of Object;



Код

    property OnEvent: TProcessorEvent read FProcessorEvent
      write FProcessorEvent;
    property OnJobState: TJobState read FJobState
      write FJobState;
    property Kvant: Integer read FKvant write SetKvant;


Код

//Вызов пользовательской TProcessorEvent
procedure TProcessor.ProcessorEvent(const Msg: String);
var
  CanStop: Boolean;
begin
//В обработчике пользователь может остановить обработку заданий,
//вернув из функции CanStop := True
  CanStop := False;
//Если доступен пользовательский обработчик - вызываем
  if Assigned(FProcessorEvent) then
    FProcessorEvent(Self,Msg,CanStop);
  if CanStop then FTImer.Enabled := False;
end;

//Вызов пользовательской TJobState
procedure TProcessor.JobState(Job: PJob);
begin
//Если доступен пользовательский обработчик - вызываем
  if Assigned(FJobState) then FJobState(Self,Job,FJobs);
end;



Добавлено @ 09:48
Для получения общего количества заданий и количества заданий заданного приоритета напишем 2 функции:

Код

//Возврат общего количества заданий в очереди
function TProcessor.JobsCount: Integer;
var
  i: Integer;
begin
  Result := 0;
  for i := Low(FJobs) to High(FJobs) do
    Result := Result + JobsCount(i);
end;

//Возврат количества заданий с заданным приоритетом
function TProcessor.JobsCount(Priority: Integer): Integer;
begin
  if (Priority<0) or (Priority>FMaxPriority)
    then Result := -1
    else Result :=  FJobs[Priority].Count;
end;



Добавлено через 3 минуты и 29 секунд
Во время обработки активного задания необходимо для всех заданий в очереди увеличить счётчик ожидания квантов.
Вынесем код в отдельную процедуру:

Код

//Изменение атрибутов всех неактивных заданий
procedure TProcessor.ChangeQueueJobs;
var
  i,j: Integer;
begin
//В этой процедуре для каждого задания
//пересчитывается квант ожидания
  for i := Low(FJobs) to High(FJobs) do
  begin
    for j := 0 to FJobs[i].Count-1
      do Inc(FJobs[i].Job[j]^.KvantWait);
  end;
end;



Добавлено через 5 минут и 4 секунды
Для расшифровки статуса задания (получения статуса в текстовом виде) напишем простую функцию:

Код

//Текстовый смысл статуса задания
function TProcessor.GetStatusStr(Status: TStatusJob): String;
begin
  case Status of
    sjWait: Result := 'Ожидает';
    sjActive: Result := 'Активно';
    sjComplete: Result := 'Завершено';
  else Result := 'Ошибка!';
  end;
end;



Добавлено через 6 минут и 2 секунды
Ну и последняя функция в реализации класса - получение задания по его идентификатору:

Код

//Поиск задания по идентификатору
function TProcessor.GetJobById(Id: Integer): PJob;
var
  i,j: integer;
begin
  Result := nil;
  for i := Low(FJobs) to High(FJobs) do
  begin
    for j := 0 to FJobs[i].Count-1 do
    begin
      if FJobs[i].Job[j]^.Id=Id then
      begin
        Result := FJobs[i].Job[j];
        Exit;
      end;
    end;
  end;
end;


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

Это сообщение отредактировал(а) Демо - 8.12.2009, 09:49


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 11:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Код

unit ufMain;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, ComCtrls, ExtCtrls, Buttons, ToolWin, StdCtrls, uDispatch;

type

  TfMainProcessor = class(TForm)
    Label1: TLabel;
    ToolBar1: TToolBar;
    sbMain: TStatusBar;
    sbAddjob: TSpeedButton;
    Panel1: TPanel;
    Label2: TLabel;
    udKvant: TUpDown;
    lbKvant: TLabel;
    Panel2: TPanel;
    lvJobs: TListView;
    tmTimer: TTimer;
    meLog: TMemo;
    procedure sbAddjobClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure udKvantChangingEx(Sender: TObject; var AllowChange: Boolean;
      NewValue: Smallint; Direction: TUpDownDirection);
    procedure tmTimerTimer(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    procedure ProcessorEvent(Sender: TObject; Msg: String; var Stop: Boolean);
    procedure JobState(Sender: TObject; Job: PJob; Jobs: TQueueJobs);
  end;

var
  fMainProcessor: TfMainProcessor;
  Processor: TProcessor;          //Экземпляр класса-процессора
  JobCompleteCounter: Integer=0;  //Счётчик завершённых заданий

implementation

{$R *.dfm}

procedure TfMainProcessor.sbAddjobClick(Sender: TObject);
var
  i: Integer;
begin
//Проверяем, создан ли экземпляр TProcessor
//Если не создан - создаём.
//Указываем максимальный приоритет. По умолчанию (второй параметр)
//выделяется 1 секунда на квант процессора
  if not Assigned(Processor) then Processor := TProcessor.Create(7);
//В панели статуса указываем количество очередей
  sbMain.Panels[2].Text := '8';
//Присваиваем пользовательские обработчики
  Processor.OnEvent := ProcessorEvent;
  Processor.OnJobState := JobState;
//СОздаём в автоматическом режиме 10 заданий в очереди
  for i := 0 to 9 do Processor.GenerateJob;
//Запускаем процессор на обработку очередей
  Processor.Start;
end;

//Обработчик записи в протокол
procedure TfMainProcessor.ProcessorEvent(Sender: TObject; Msg: String;
  var Stop: Boolean);
begin
//Записываем в протокол сообщение процессора
  meLog.Lines.Add(Msg);
end;

//Обработчик изменения состояния задания
procedure TfMainProcessor.JobState(Sender: TObject; Job: PJob; Jobs: TQueueJobs);
var
  i: Integer;
  LI: TListItem;
  isFound: Boolean;
  pj: PJob;
  ActiveCounter: Integer;
begin
//isFound - флаг-признак того, что нужный элемент найден в цикле
  isFound := False;
  for i := 0 to lvJobs.Items.Count-1 do
  begin
  //Ищем в нашем ListView переданное задание (Job)
    if lvJobs.Items[i].Caption=IntToStr(Job^.Id) then
    begin
    //Заполняем столбцы ListView
      LI := lvJobs.Items[i];
      LI.SubItems[3] := IntToStr(Job^.KvantActive);
      LI.SubItems[4] := IntToStr(Job^.KvantWait);
      LI.SubItems[5] := IntToStr(Job^.KvantLast);
      LI.SubItems[6] := TProcessor(Sender).GetStatusStr(Job^.Status);
      isFound := True;
      Break;
    end;
  end;

//Если задание не нашлось в ListView,
//значит оно новое. Добавляем.
  if not isFound then
  begin
    LI := lvJobs.Items.Add;
    LI.Caption := IntToStr(Job^.Id);
    LI.SubItems.Add(IntToStr(Job^.Priority));
    LI.SubItems.Add(IntToStr(Job^.FullTime));
    LI.SubItems.Add(FormatDateTime('hh:nn:ss',Job^.StartTime));
    LI.SubItems.Add(IntToStr(Job^.KvantActive));
    LI.SubItems.Add(IntToStr(Job^.KvantWait));
    LI.SubItems.Add(IntToStr(Job^.KvantLast));
    LI.SubItems.Add(TProcessor(Sender).GetStatusStr(Job^.Status));
  end;


//Обновляем остальные задания
//В цикле считываем все очереди, находим по Id сами задания и
//обновляем информацию в ListView
  for i := 0 to lvJobs.Items.Count-1 do
  begin
    pj := TProcessor(Sender).GetJobById(StrToInt(lvJobs.Items[i].Caption));

//Если задание не найдено, значит оно уже завершено
//Для найденных заполняем соответствующие поля
    if Assigned(pj) then
    begin
      LI := lvJobs.Items[i];
      LI.SubItems[3] := IntToStr(pj^.KvantActive);
      LI.SubItems[4] := IntToStr(pj^.KvantWait);
      LI.SubItems[5] := IntToStr(pj^.KvantLast);
      LI.SubItems[6] := TProcessor(Sender).GetStatusStr(pj^.Status);
    end;
  end;

//Получаем количество заданий в очередях
  ActiveCounter := TProcessor(Sender).JobsCount;
//Проверяем статус полученного в обработчике задания.
//Если задание завершено - увеличиваем счётчик завершённых,
//иначе - счётчик активных
  if Job^.Status=sjComplete
    then Inc(JobCompleteCounter)
    else Inc(ActiveCounter);

//Заполняем поля на панели статистики
  sbMain.Panels[4].Text := IntToStr(ActiveCounter);
  sbMain.Panels[6].Text := IntToStr(JobCompleteCounter);
  sbMain.Panels[8].Text := IntToStr(Job^.Priority);
end;

procedure TfMainProcessor.FormCreate(Sender: TObject);
begin
//Сразу показываем текущее время на панели статистики
  tmTimerTimer(Self);
//Изменяем заголовок приложения, чтобы на панели задач
//отображался во всплывающей подсказке именно этот заголовок
  Application.Title := fMainProcessor.Caption;
//Устанавливаем двойную буферизацию
//для оптимизации обновления ListView
  lvJobs.DoubleBuffered := True;
end;

//Динамическое изменение кванта времени
//Диапазон ограничен - от 0 до 9
procedure TfMainProcessor.udKvantChangingEx(Sender: TObject;
  var AllowChange: Boolean; NewValue: Smallint;
  Direction: TUpDownDirection);
begin
  if (NewValue>=udKvant.Min) and (NewValue<=udKvant.Max) then
  begin
    lbKvant.Caption := IntToStr(NewValue);
    if Assigned(Processor) then Processor.Kvant := NewValue;
  end;
end;

procedure TfMainProcessor.tmTimerTimer(Sender: TObject);
begin
  sbMain.Panels[0].Text := FormatDateTime('dd.mm.yyyy hh:nn:ss',Now);
end;

procedure TfMainProcessor.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
//Освобождаем выделенную память, уничтожаем все объекты.
  Processor.Free;
end;

end.


Добавлено @ 11:27
Основная форма будет выглядеть так:
user posted image


Это сообщение отредактировал(а) Демо - 8.12.2009, 11:28


--------------------
    
PM MAIL ICQ Skype   Вверх
Демо
Дата 8.12.2009, 11:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1278
Регистрация: 3.11.2005

Репутация: 7
Всего: 50



Ну и, наконец-то, - весь проект  целиком.



Это сообщение отредактировал(а) Демо - 8.12.2009, 11:35

Присоединённый файл ( Кол-во скачиваний: 15 )
Присоединённый файл  DispatchProcess.zip 7,46 Kb


--------------------
    
PM MAIL ICQ Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Delphi: Для новичков"
SnowyMetalFan
bemsPoseidon
Rrader

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Литературу по Дельфи обсуждаем здесь
  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь
  • 90% ответов на свои вопросы можно найти в DRKB (Delphi Russian Knowledge Base) - крупнейшем в рунете сборнике материалов по Дельфи


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Delphi: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0767 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.