[Delphi] Программа Life
я не понял, какую величину ты хочешь оценить?
Если нужно оценить величину задержи между итерациями, то это ИМХО всегда делается "на глаз"...
или во время хода программы?
Заранее оценить очень сложно - т.к. зоопарк техники довольно сильно отличается.
Если по ходу работы:
то замеряешь сколько времени у тебя работает функция Idle,
а также через сколько времени у тебя получает управление функция Idle.
На основе отношения этих двух чисел - выбираешь время паузы.
Мне сразу стало интересно как же она работает - ну не в бесконечный же цикл все засовывать? Я думал, что этим занимается отдельный поток с пониженным приоритетом.
Но здесь как я понял идет следующее:
Выполняются необходимые изменения во всех экземплярах объектов пищи, животных и растений (настолько быстро, насколько может процессор)
Если прошло менее двадцати миллисекунд, ничего не предпринимается
Т.е. получается, что все-все-все укладывается в этот промежуток. Это значение жестко зафиксировано в программе. Т.е. если компьютер будет слабее (т.е. выполнит код дольше). то получится ошибка?
Я почитал справку про OnIdle, но не понял когда точно оно наступает и есть ли альтернатива этому ходу при программировании жизни...
OnIdle срабатывает "после каждого пука" в цикле обработке сообщений приложения (см. Applicatio.Run)
Есть мнение, что пользоваться OnIdle - признак плохого программирования.
Не совсем.
Сценарий такой:
Пересчитывается изменения
делается пауза в 20 мс
Опять пересчитываются изменения
и т.д.
В данном случае - есть следующие потенциальные проблемы:
1. в секунду будет не больше 50 шагов, хотя может быть процессор может выполнить 1000 шагов за секунду.
2. Программа будет тормозить, если остальные действия программы не укладываются в паузу из 20 мс.
3. Будет эффект подвисания, если время пересчитывания изменений очень велико (например, несколько секунд).
> то получится ошибка?
будет эффект подвисания, т.е. действия пользователя программа будет выполнять с большой задержкой
> Я почитал справку про OnIdle, но не понял когда точно оно наступает
Он наступает, когда с точки зрения программы все остальные команды/действия выполнены.
> есть ли альтернатива этому ходу при программировании жизни...
Альтернатива - отдельный тред, но отдельный тред сложнее в программировании.
OnIdle вызывается, если в очереди сообщений нет других сообщений.
It is not called continuously unless the Done parameter is set to falseА вот это можно пояснить? Т.е. она НЕ вызывается повторно кроме случаев когда присваивается false.А в коде в конце процедуры - там true, как же она тогда дальше работает?
Возникает вопрос: что будет возвращать эта функция через (MaxLongInt/1000/60/60/24) т.е. примерно через двадцать пять дней? Ясно, что система может проработать и двадцать пять дней и пятдесят...
longint переполнится и будет возвращаться время опять начиная с нуля
PS. А вообще какая нафиг разница, пусть переполняется. Если с ним только это
делать, то единственное, что должно беспокоить - чтобы между двумя OnIdle двадцать пять дней не прошло
This-Last > 20
потому что событие происходит примерно раз в 49 дней (при условии непрерывной работы ОС но все же может произойти, а прога рискует подвиснуть...
И использовать надо его.
Читайте исходники VCL - многие вопросы отпадут. Лучшего хелпа просто не существует.
2, :
В Applicatio.Run бесконечный цикл, обрабатывающий приходящие сообщения.
1. При приходе сообщения оно обрабатывается,
2. после обработки срабатывает OnIdle и будет вызываться пока не вернет Done = True (попутно обрабатывая приходящие сообщения).
3. Как только OnIdle вернет Done = True, программа прекращает вызывать OnIdle пока снова не придет какое-нть сообщение.
4. Далее см. п.1.
Запости, пожалуйста, код функции Run, где OnIdle вызывается.
То получится более правильный сценарий - то есть каждая итерация занимает не менее 20 мс, если времени не хватает, то ничего страшного не происходит, просто прога работает медленнее. Но тут уж ничем не поможешь, а всякие треды с таймерами только ухудшат ситуацию.
This:=GetTickCount;
if (This-Last)> 20 then
begin
Last:=GetTickCount; // Типа местами поменял
(итерация жизни - несколько вложенных процедур, устанавливающих параметры живности)
end;
По поводу "раз в 49 дней" - правильно сказал - всё и так зашибись, ничего менять не нужно, разве что действительно проверить, что тип у переменных одинаковый.
По поводу OnIdle - исходников VCL у меня под рукой нету, равно как и желания в них смотреть. Но! Все нормальные люди конечно же понимают, что OnIdle вызывается когда сообщения _кончились_. То есть они все обрабатываются, обрабатываются, а вот когда PeekMessage вернёт false - вызовется OnIdle. Насчёт параметра - не знаю, но выглядит логично.
Хотя аффтары дельфей - известные долбы, они могли и извратить смысл OnIdle вызывая её после каждого сообщения. Хотя это уж совсем полными долбами надо быть.
OnIdle по смыслу вызывается не постоянно если очередь сообщений пустая, а только тогда один раз, когда приложение очищает очередь. Для чего они туда прикрутили Done - не разбирался. Но логику работы я описал. Исходники запощу с работы..
procedure TApplication.Run;
begin
...
repeat
try
HandleMessage;
except
HandleException(Self);
end;
until Terminated;
...
end;
procedure TApplication.HandleMessage;
var
Msg: TMsg;
begin
if not ProcessMessage(Msg) then Idle(Msg);
end;
procedure TApplication.Idle(const Msg: TMsg);
begin
...
try
if Assigned(FOnIdle) then FOnIdle(Self, Done);
if Done then DoActionIdle;
except
HandleException(Self);
end;
if (GetCurrentThreadID = MainThreadID) and CheckSynchronize then
Done := False;
if Done then WaitMessage;
end;
CheckSynchronize в однопоточном приложении всегда False
ProcessMessage и WaitMessage как выглядит?
function TApplication.ProcessMessage(var Msg: TMsg): Boolean;
begin
Result := False;
if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then
begin
Result := True;
...
end;
end;
function WaitMessage; external user32 name 'WaitMessage';
The WaitMessage function yields control to other threads when a thread has no other messages in its message queue. The WaitMessage function suspends the thread and does not return until a new message is placed in the thread's message queue.
BOOL WaitMessage(VOID)
Parameters
This function has no parameters.
Return Values
If the function succeeds, the return value is nonzero.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
BOOL PeekMessage(
LPMSG lpMsg, // pointer to structure for message
HWND hWnd, // handle to window
UINT wMsgFilterMin, // first message
UINT wMsgFilterMax, // last message
UINT wRemoveMsg // removal flags
);
...
Return Values
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.
Remarks
Unlike the GetMessage function, the PeekMessage function does not wait for a message to be placed in the queue before returning.
...
ps
Да, согласен, с _Fj в Дельфи Idle реализован очень и очень странно.
Оставить комментарий
loks-po
Существует одна из версий "жизни", в которой писавший товарисч (Савченко Павел, МИФИ, 2004) немного изменил начальные условия классической формулировки (приведу на всякий случай)Прога работает отрисовывая всю эту живность кружочками разных цветов на форме.
События начинаются по OnIdle формы.
В чем состоит вопрос: в коде я оставил только волнующий меня момент. Итерация жизни выполняется только если прошло 20 миллисекунд между двумя заходами в OnIdle.
Как можно оценить эту величину? ( Мне нужно написать нечто похожее) Как я понимаю она зависит от того, сколько кода должно выполняться внутри... но хотелось бы знать вообще что называется "порядок цифр".