Планировщик
От: unga  
Дата: 05.10.09 20:11
Оценка:
Убедительная просьба не пинать, т.к. с такой задачей столкнулся впервые.
И поиск каких то ощутимых ответов не принес.
Возникла необходимость написать нечто наподобие планировщика(именно на здесь возник вопрос).
т.е. необходимо отслеживать время наступления енного количества задач и произвести соответствующее каждой из них действие.
Количество задач заранее неизвестно(может быть и 1 и 500).

Собственно вопрос в том как организовать процесс отслеживания.

Вариант 1. создать для каждой задачи свой Timer.
Но возникает вопрос: сколько таймеров выдержит система? как это отразится на нагрузке?

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

Вариант 3. найти готовый компонент.

Пока пытаюсь решить задачу по 3-му варианту.
Но найти пока не удается.
И есть сомнения: насколько готовая компонента ограничит возможности?
есть ли вообще такие?
И по какому варианту в них реализована обработка?

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

Заранее благодарен за любую помошь!
Re: Планировщик
От: Hruks Россия www.hruks.com
Дата: 05.10.09 22:45
Оценка:
U>т.е. необходимо отслеживать время наступления енного количества задач и произвести соответствующее каждой из них действие.
U>Количество задач заранее неизвестно(может быть и 1 и 500).
U>Вариант 3. найти готовый компонент.
RXTimer из пакета RX компонент.
Работает по второму варианту, описанному Вами.
Программный интерфейс: из кода создаются "виртуальные" таймеры, для программы они выглядят как обычные таймеры — можно выставить интервалы времени и обработчики события OnTimer.

Первый вариант не подходит, так как кол-во таймеров в системе — ресурс очень ограниченный.
Re[2]: Планировщик
От: soloton  
Дата: 06.10.09 07:24
Оценка: +1
Я бы сделал один таймер, но установил его на ближайшее событие.

Перебираем все задачи, находим самую первую по времени. Устанавливаем интервал таймера до этой задачи (время задачи минус текущее время)
Когда задача выполнилась, делаем переопределение интервала по тому же алгоритму.
Также не забываем менять значение интервала при добавлении/удалении/изменении задачи.

И никаких лишних компонентов.
Re: Планировщик
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 06.10.09 07:49
Оценка:
Здравствуйте, unga, Вы писали:

U>Возникла необходимость написать нечто наподобие планировщика(именно на здесь возник вопрос).

U>т.е. необходимо отслеживать время наступления енного количества задач и произвести соответствующее каждой из них действие.
U>Количество задач заранее неизвестно(может быть и 1 и 500).

U>Собственно вопрос в том как организовать процесс отслеживания.


Вот пример простенького планировщика. Надеюсь подойдет в качестве стартовой точки.


type
  PCronItem=^TCronItem;         // информация о времени запуска задачи. Можно расширить при необходимости.
  TCronItem=packed record
    Day,Month,Year:word;
    Hour,Min,Sec  :word;
  end;

  TOnCronEvent=procedure (Sender:TObject; Item:TCronItem) of object;  // обработка момент наступления "времени Ч"

  TCron=class(TList)            // список времени задач для запуска
  private
    FOnCronEvent: TOnCronEvent;
  public
    procedure AddCron(Item:TCronItem);  // добавляем новую задачу
    procedure ScanCronItems;            // сканируем на предмет "времени Ч"
    procedure Clear; override;          // чистимся 
    Constructor Create; virtual;        
    property OnCron:TOnCronEvent read FOnCronEvent write FOnCronEvent;
  end;


{ TCron }

procedure TCron.AddCron(Item: TCronItem);
var P:PCronItem;
begin
  New(P);
  P^:=Item;
  Add(P);
end;

procedure TCron.Clear;
var i:integer;
    Item:PCronItem;
begin
  for i:=0 to Pred(Count) do
    begin
      Item:=PCronItem(Self.Items[i]);
      if Item=nil then continue;
      Items[i]:=nil;
      Dispose(Item);
    end;
  inherited;
end;

constructor TCron.Create;
begin
  inherited;
  FOnCronEvent:=nil;
end;

function CheckCronItem(Item:TCronItem; AYear,AMonth,ADay,AHour,AMinute,ASecond:word):boolean;
begin
 if (Item.Day<>0) and (Item.Month<>0) and (Item.Year<>0) then        // если у времени задачи указана дата, то проверяем наступление даты
     Result:=(Item.Day=ADay) and (Item.Month=AMonth) and (Item.Year=AYear)
 else Result:=True;   // если дата не указана - сканируем только время
 if Result then
    Result:=Result and (Item.Hour<=AHour) and (Item.Min<=AMinute) and (Item.Sec<=ASecond);  // время уже наступило 
end;

procedure TCron.ScanCronItems;
var i:integer;
    Item:PCronItem;
    AYear,AMonth,ADay,AHour,AMinute,ASecond,AMilliSecond:Word;
begin
 DecodeDateTime(Now,AYear,AMonth,ADay,AHour,AMinute,ASecond,AMilliSecond);  // хватаем текущее время
 for i:=Pred(Count) downto 0 do
   begin
     Item:=PCronItem(Items[i]);
     if Item=nil then continue;   
     if CheckCronItem(Item^,AYear,AMonth,ADay,AHour,AMinute,ASecond) then
      begin
        if Assigned(FOnCronEvent) then  FOnCronEvent(Self,Item^);  // время !!!
        Items[i]:=nil;  // раз мы уже обработали этот элемент - он нам больше не нужен
        Dispose(Item);
        Delete(i);
      end;
   end;
end;



Ну и соответственно для запуска достаточно в ApplicationEvents.OnIdle() сделать проверку событий и обработать момент наступления события:


var MyCron:TCron;  // список задач...

Procedure MainForm.HandleCronEvent(Sender: TObject; Item: TCronItem);
begin
   FireTimedWork(Item, .....);   //  тут псевдометод - ну не знаю я, что там за задачи будут запускатся
end;

Procedure MainForm.InitCron;     // инициализация
var CItem:TCronItem;
begin
   MyCron:=TCron.Create;           // создаем список
   MyCron.OnCron:=HandleCronEvent; // устанавливаем обработчик 
   CItem.Day:=0; CItem.Month:=0; CItem.Year:=0;  // нет даты
   CItem.Hour:=12; CItem.Min:=0; CItem.Sec:=0;   // время 12:00:00
   MyCron.AddCron(CItem);          // добавляем время срабатывания на "сегодня в 12:00:00"
end;

Procedure MainForm.ApplicationOnIdle(Sender: TObject);
begin
   MyCron.ScanCronItems;     // ждем "времени Ч" когда приложения ничем не занято... Собственно тоже самое можно делать и по таймеру...
end;
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re[2]: Планировщик
От: Aniskin  
Дата: 06.10.09 19:57
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>
DM>Procedure MainForm.ApplicationOnIdle(Sender: TObject);
DM>begin
DM>   MyCron.ScanCronItems;     // ждем "времени Ч" когда приложения ничем не занято... Собственно тоже самое можно делать и по таймеру...
DM>end;


IMHO, лучше вызывать MyCron.ScanCronItems в OnTimer, потому-что после вызова MyCron.ScanCronItems приложение уйдет в цикл ожидания оконного сообщения, которое в теории может не прийти очень долго, и поэтому можно проспать очередной cron.
Re[3]: Планировщик
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 07.10.09 07:33
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>IMHO, лучше вызывать MyCron.ScanCronItems в OnTimer, потому-что после вызова MyCron.ScanCronItems приложение уйдет в цикл ожидания оконного сообщения, которое в теории может не прийти очень долго, и поэтому можно проспать очередной cron.


Ну, в коментариях я это и указал... И OnIdle() приходит тоже достаточно часто. Даже чаще чем OnTimer()
WBR, Dmitry Beloshistov AKA [-=BDS=-]
Re[3]: Планировщик
От: Dimonka Верблюд  
Дата: 07.10.09 07:55
Оценка:
Здравствуйте, Aniskin, Вы писали:

A>IMHO, лучше вызывать MyCron.ScanCronItems в OnTimer, потому-что после вызова MyCron.ScanCronItems приложение уйдет в цикл ожидания оконного сообщения, которое в теории может не прийти очень долго, и поэтому можно проспать очередной cron.


По сути, если задачи отсортировать по времени, то сканировать их и не требуется, требуется только определить, сколько задач уже "созрело" на текущий момент.
Re[4]: Планировщик
От: Aniskin  
Дата: 07.10.09 16:59
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>Ну, в коментариях я это и указал...


Термин _можно_ отличается от термина _лучше_

DM>И OnIdle() приходит тоже достаточно часто. Даже чаще чем OnTimer()


Как часто приходят сообщения приложению, если его основное окно скрыто, а таким скорее всего и будет приложение-планировщик? Вероятно, не очень часто, только какие-нибудь служебные (сам я это не проверял, поэтому в случае чего прошу меня поправить). Т.е. если в 20:00 поставить задачу на 20:05 и отлучиться от компьютера, то к 20:05 приложение-планировщик будет находиться в цикле ожидания сообщения, и молоко убежит.
Re[4]: Планировщик
От: Danchik Украина  
Дата: 07.10.09 18:37
Оценка:
Здравствуйте, DarkMaster, Вы писали:

DM>Здравствуйте, Aniskin, Вы писали:


A>>IMHO, лучше вызывать MyCron.ScanCronItems в OnTimer, потому-что после вызова MyCron.ScanCronItems приложение уйдет в цикл ожидания оконного сообщения, которое в теории может не прийти очень долго, и поэтому можно проспать очередной cron.


DM>Ну, в коментариях я это и указал... И OnIdle() приходит тоже достаточно часто. Даже чаще чем OnTimer()


По перформансу лучше пользоваться такими связками:

CreateWaitableTimer
SetWaitableTimer — ставим таймер на время следующего срабатывания
WaitForSingleObject — ждем срабатывания в потоке
Re[5]: Планировщик
От: DarkMaster Украина http://www.bdslib.at.ua
Дата: 08.10.09 08:32
Оценка:
Здравствуйте, Aniskin, Вы писали:

DM>>Ну, в коментариях я это и указал...

A>Термин _можно_ отличается от термина _лучше_

термин _флейм_ тоже отличается от термина _пример кода_... Все зависит от задачи.

DM>>И OnIdle() приходит тоже достаточно часто. Даже чаще чем OnTimer()

A>Как часто приходят сообщения приложению, если его основное окно скрыто, а таким скорее всего и будет приложение-планировщик? Вероятно, не очень часто,

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

Я привел код (рабочий) простого планировщика как отправной точки. Методов вызова сканирования списка задач, организации самого списка, информации для списка и т.п. — множество. В дополнение я привел один из примеров использования. В дальнейшем — каждый волен поступать так, как ему будет удобно с приведенным кодом. Или использовать, или выбросить, или доработать напильником.
WBR, Dmitry Beloshistov AKA [-=BDS=-]
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.