|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Зависает программа
Есть некий код, при выполнении которого программа зависает часа на два, пока не выполнится цикл. Знаю что можно этот цикл запихнуть в отдельный поток, а как это сделать не знаю, с потоками никогда не работал. Подскажите пожалуйста как это лучше сделать. Заранее спасибо.
Код:
procedure TMyForm.PuskButClick(Sender: TObject); var i, j, k: Integer; s, str, sum, xOut, xOut1, StrAdr: String; x: Byte; pop: array of Byte; WritteBuffer: array[1..19] of Byte; ReadBuffer: array[1..21] of Byte; BytesWritten, BytesRead: DWORD; begin if PortConnect = True then if InitModem = True then begin for i := 1 to 4094 do begin ................................................................. //здесь происходит преобразование посылки в СОМ порт ................................................................. WriteFile(Port, WritteBuffer[1], Length(WritteBuffer), BytesWritten, nil); ReadFile(Port, ReadBuffer, SizeOf(ReadBuffer), BytesRead, nil); ................................................................. //здесь происходит преобразование ответа из СОМ порта ................................................................. end; end; |
#2
|
||||
|
||||
Может просто по таймеру (компонент TTimer)? В свойстве Interval выставить нужный интервал (в мс) между срабатываниями таймера.
Чтоб сделать такое, нужно отказаться от конструкций "for" или "while" и прописать всё руками. Т.е. начальное состояние (i := 1) задаётся в основной программе, а инкремент переменной и опосля проверка на достижение предела, а также основные действия (преобразование посылки в СОМ порт и т.п.), прописывается в обработчике OnTimer. Таймер сразу не запускаете, сначала инициализируете переменные. Если надо - остановить таймер можно как из основной программы, так и в самом обработчике, при выполнении каких-либо условий. Последний раз редактировалось Guaho, 21.04.2020 в 15:09. |
#3
|
|||
|
|||
А как я смогу выставить интервал, если я не знаю когда придет ответ из СОМ порта, он может придти через секунду, а может через пять.
|
#4
|
||||
|
||||
Поставить малый интервал - скажем, 50 мс, тут зависит от самой задачи, насколько критичен интервал между наступлением события и началом его обработки. Ответ из порта как-то ведь проявляет себя, т.е. программно же можно определить, что он пришёл? Если да, то ставите проверку - есть ответ? Если нет, выход (т.е. "холостой ход" кода обработки приёма ответа порта).
|
#5
|
|||
|
|||
Простой вариант: в цикл вставить вызов Application.ProcessMessages, тогда программа хоть со скрипом (в зависимости от длительности итерации цикла) будет реагировать на действия пользователя.
Сложный вариант: действительно оформить цикл как отдельный поток и сделать ассинхронный вызов, т.е. программа запускает поток, потом по таймеру проверяет, закончился ли поток. Простой вариант реализации: Поток, в отдельном модуле: Код:
unit WrkThread; interface type TMyThread = class(TThread) private FIsRunning : Boolean; FParam : String; // Какие-то данные для потока public procedure Execute; override; property IsRunning : Boolean read FIsRunning; property Param : String read FParam write FParam; end; implementation procedure TMyThread.Execute; begin FIsRunning := True; Try For I := 1 To 4094 Do // Твой цикл Begin If Terminated Then Break; // Для того, что бы можно было прервать поток // Далее твой код End; Finally FIsRunning := False; End; end. А теперь вызов: Код:
type TForm1 = class(TForm) private T : TMyThread; // В OnCreate или в конструкторе присвоить Nil // В деструкторе или в OnDestroy уничтожить если <> Nil end; ... procedure TForm1.Button1Click(Sender : TObject); begin If T = Nil Then T := TMyThread.Create(True); If T.IsRunning Then ShowMessage('Поток уже работает!') Else Begin T.Param := 'aaa'; // передали данные потоку T.Resume; // запускаем поток Timer1.Enabled := True; // Запускаем таймер для проверки состояния потока End; end; procedure Timer1Timer(Sender : TObject); begin If T = Nil Then Timer1.Enabled := False // Защита Else If Not T.IsRunning Then // Поток закончился Begin Timer1.Enabled := False; // выключаем таймер FreeAndNil(T); // Удаляем поток, что бы не болтался ShowMessage('Поток завершился'); End; end; Это грубый пример, просто что бы показать как это все работает. |
#6
|
|||
|
|||
Сделал через таймер, вполне устраивает, даже можно остановить выполнение. Прогу пишу для себя, поэтому наверное так и оставлю, хотя понимаю что это не правильно. Просто учиться на 5ом десятке уже в лом, хоть и карантин. Спасибо огромное, что помогаете.
|
#7
|
|||
|
|||
Таймер хорошо, если обработка укладывается в интервал срабатывания таймера. Если нет, то могут полезть глюки, которые будет тяжело ловить, бо как они будут лезть на первый взгляд в случайный момент.
Тоже самое с Application.ProcessMessages в теле цикла. Там интервал (время выполнения одной итерации цикла) определяется человеком, т.е. для человека должно казаться, что программа работает без зависаний. Это где-то 30-50 мс на выполнение одной итерации цикла. Во всех остальных случаях - отдельный поток или процесс. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Guaho (22.04.2020)
|
#8
|
|||
|
|||
Я просто в обработчике событий таймера сперва его отключаю, выполняю преобразования, посылаю в СОМ порт, получаю ответ, его преобразовываю и включаю обратно таймер. Приложение хоть как то реагирует. И даже можно остановить запросы.
|
Этот пользователь сказал Спасибо rodionov_uv за это полезное сообщение: | ||
Guaho (22.04.2020)
|
#9
|
|||
|
|||
Угу. Особенно надо обратить внимание на слова "хоть как то реагирует".
Опять же, если прога пишется для себя, то и так сойдет. Но если придется передавать кому-то еще, то придется менять. ЗЫ. А вообще, лучше привыкать сразу делать правильно. Лучше набить шишек на простых примерах, а в дальнейшем уже будешь знать как делать правильно... Но это мое мнение, для себя решай сам. |