gonzales
Пользователи-
Постов
334 -
Зарегистрирован
-
Посещение
-
Победитель дней
27
Весь контент gonzales
-
Спасибо! До меня начало доходить, нужно полностью отойти от метода программирования, когда в коде основной формы пишутся все процедуры и функции, все выносить в отдельные юниты, в которых в потоках (потоке) обрабатывается вся логика и лишь через синхронизацию выдает информацию в главный поток. А главный поток по сути свободен, занят лишь перерисовкой форм и получением событий мыши.
-
Ну да уж))) Я читал этот Ваш пост и вот этот http://www.cyberforum.ru/blogs/469693/blog4909.html, натыкался на него еще до этого обсуждения. Тем не менее я считаю, что использование ProcessMessages в простых случаях оправдано. Ведь это ни что иное, как изменение приоритетов процессов, например когда нужно просто перерисовать форму, с помощью ProcessMessages останавливаем текущий процесс и запускаем процессы в очереди. Но это сугубо ИМХО. Зачем было убирать его мне не понятно, хочешь используй, не хочешь - не используй, делай костыли, как предложил IS1 со слипами, потоками и прочими радостями. С потоками мне все понятно, сам их активно использую, например при раскодировании изображений с ip-камер. Но как их использовать, когда есть очередность действий в главном потоке? Не сочтите за труд, я думаю это многим было бы интересно, покажите, как переписать пример выше используя Вашу концепцию.
-
Прошу прощения за долгое отсутствие Вот накидал простой исходникtest10.3.zip. В проекте 3 формы, первой создается SplashForm. Остальные формы имеют в OnCreate высоконагруженные вычисления, поэтому после каждого создания формы отображаю на SplashForm статус формы. Дабы все это отрисовывалось использую Application.processmessages.
-
sleep здесь исключительно для наглядности, и вообще весь код. Реальная задача: имеется окно заставки, на нем отображается процесс загрузки приложения. Не могу добиться нормального функционирования, потоки не помогают. Разве что действительно таймер использовать)))
-
На Андроиде тоже не работает.
-
Можно. Но этот выход не выход))) procedure TForm1.Button1Click(Sender: TObject); var a,b:integer; begin a := 1; b := 2; label1.Text:=inttostr(a); //application.ProcessMessages; sleep(2000); a := a + b; label1.Text:=inttostr(a); //application.ProcessMessages; sleep(2000); a := a + b; label1.Text:=inttostr(a); //application.ProcessMessages; sleep(2000); end; Боюсь представить, какой код будет в таком случае
-
Спасибо, идею я понял, надо мне ее осмыслить. А что делает главный поток, пока класс ждет ответа от потока? Вот, например, инициализация устройств, я шлю команду "КОЛ-ВО_УСТРОЙСТВ", получаю ответ, допустим 5, далее цикл от 1 до 5 с командой "ОПРОС_УСТРОЙСТВА", получаю ответ "ОК", отдаю в интерфейс информацию. В Вашем примере получается, я создаю в основном потоке очередь сообщений из одной команды, отправляю ее в класс, который эту команду из очереди отправляет в поток, а вот дальше не понятно, класс ждет ответа, или может формировать какие-то еще очереди команд? Ну допустим есть какой-то коллбэк из потока в класс, вернулась информация о кол-ве устройств, кто дальше должен сформировать очередь команд на опрос устройств? Класс отдает информацию в главный поток, а он ведь уже трудится дальше и не ждет прихода информации от класса, или ждет? Тогда зачем поток?
-
Спасибо за поддержку! Суть в следующем, есть сервер (самописный TCP), крутится на микроконтроллере, есть клиенты (в основном Android, iOS). К серверу подключен ряд устройств. Клиенты управляют сервером посредством управляющих команд (конфигурируют устройства, настраивают логику работы и т.д), и получают ответы об изменениях в устройствах. Был разработан некий универсальный протокол общения (id адресата, id отправителя, id команды, параметры) в 15 байт. То есть клиент посылает пакет в 15 байт, и сервер отвечает пакетом в 15 байт. На текущий момент есть около 100 команд для общения. Например для конфигурирования устройства я посылаю команду с указанием типа устройства, после получения положительного ответа, посылаю команду с уставкой, после получения ответа посылаю команду на старт, и так далее. Также сервер рассылает информацию клиентам об изменениях в устройствах. В общем все работает в одном потоке, но если какое-то из устройств не ответит вовремя, то возникает задержка, из-за чего хочется запихнуть все общение на клиенте в поток. Надеюсь понятно расписал, но мог что-то уточнить.
-
да, еще создаем вставки на асме, , всего стопиццот строк кода и всего делов Уже неделю вспахиваю интернет, но пока ничего внятного.
-
Может я совсем тупой, но я не могу этого понять. Вот банальный пример, одна кнопка, один лэйбл. Вот весь код unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Label1: TLabel; Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.Button1Click(Sender: TObject); begin label1.Text:='2'; //application.ProcessMessages; sleep(5000); label1.Text:='3'; end; end. Как заставить это все работать без ProcessMessages? Хочу увидеть при нажатии на кнопку "2", затем "3". Пипец задачка(((((
-
также не работает((( procedure TSplashForm.LoadProgramm; begin Application.CreateForm(TForm1, Form1); tthread.CreateAnonymousThread( procedure begin // Асинхронная логика sleep(100); tthread.Synchronize(nil, procedure begin StartUpLabel.Text := 'Инициализация!!!'; Label1.Text := VERSION; end); end).Start; end; Вот кусок кода, LoadProgramm вызывается при показу Splash формы. Я хочу после создания формы Form1 показать на сплэше текст "Инициализация!!!"
-
надо делать какие-то callback функции, каким-то образом их менять в зависимости от ситуации, в общем скила мне не хватает(
-
Есть у меня такой. Проблема не в идентификации ответа, а в очередности прихода сообщений, как выполнить следующий запрос на основании ответа от предыдущего, если я не знаю, когда его (ответ) получу, и какой он будет в очереди.
-
Проблема в том, что клиентов может быть много, соответственно приходов много))), и то, что флаг поднят, еще не значит, что это нужный ответ. Вот над этим я думаю постоянно, но внятного решения пока не вижу. Например мне нужно послать один запрос, получить ответ и на основании этого ответа послать еще тот или иной запрос. при линейной логике, запрос-ответ, запрос-ответ, это работает, а если у меня есть асинхронный поток, в который сыплется очередь из сообщений в которой есть "нужные" ответы, как организовать правильную логику работы - я пока не придумал. Если есть какие-нибудь соображения, буду благодарен?
-
Спасибо за пример, но по ходу я нуб 80 уровня, я не вижу, чем он может мне помочь. Посмотрите плиз конструкцию, она работает очень криво, но я не могу понять почему Вот класс type TClusterClient = class(TThread) public procedure ReadPacket(HTTPAnswer: THTTPAnswer); procedure ChechClient; protected procedure Execute; override; end; procedure TClusterClient.ChechClient; var Len: integer; Buf: TIdBytes; H: THTTPAnswer; begin if form1.Client1.Socket.InputBufferIsEmpty = false then begin try SetLength(Buf, SizeOf(THTTPAnswer)); form1.Client1.Socket.ReadBytes(Buf, SizeOf(THTTPAnswer), false); Move(Buf[0], H, Length(Buf)); ReadPacket(H); finally SetLength(Buf, 0); end; end; end; procedure TClusterClient.Execute; begin while not Terminated do begin try if form1.Client1.Connected then begin if form1.Client1.Socket.CheckForDataOnSource(100) then begin ChechClient; end; end; except end; end; end; procedure TClusterClient.ReadPacket(HTTPAnswer: THTTPAnswer); var t_out, t_in: TIdBytes; Size: TSizeF; i, j: integer; l: integer; // d, a: byte; VirtualNode: IXMLNode; VirtualElementNode: IXMLNode; c: cardinal; s: string; begin if (HTTPAnswer.Preamble[0] = 67) and (HTTPAnswer.Preamble[1] = 76) and (HTTPAnswer.Preamble[2] = 85) then begin TThread.Synchronize(nil, procedure begin form1.AnswerIsComming := HTTPAnswer.Command; form1.HTTPAnswer := HTTPAnswer; end); end else if (HTTPAnswer.Preamble[0] = 85) and (HTTPAnswer.Preamble[1] = 80) and (HTTPAnswer.Preamble[2] = 68) then begin if HTTPAnswer.Command = HTTP_SEND_ELEMENT_UPDATE then begin for i := 0 to Length(form1.RoomElements) - 1 do begin if HTTPAnswer.Data10 = 1 then // устройства begin if (form1.RoomElements[i] is TRele) { and (MasterTask.Status <> TTaskStatus.Canceled) } then begin TThread.Synchronize(nil, procedure var d, a: byte; begin d := (form1.RoomElements[i] as TRele).Device_ID; a := (form1.RoomElements[i] as TRele).Device_Adress; if (d = HTTPAnswer.Device_ID) and (a = HTTPAnswer.Device_Adress) then form1.device[d].Board[a].CurrentValue := HTTPAnswer.Data2; (form1.RoomElements[i] as TRele).ReleSwitch.IsChecked := form1.device[d].Board[a] .CurrentValue.ToBoolean; form1.ConnectImage.Bitmap := form1.IconList.Bitmap(Size, 15); end); end; end; end; end; end; end; А вот запрос информации с сервера из главного потока procedure TForm1.SendHTTPMessage(ReadTimeout: integer); var t_out, t_in: TIdBytes; Size: TSizeF; begin TriesToSend := 0; if ReadTimeout = -1 then begin if LocalIP = true then begin Client1.ConnectTimeout := 500; Client1.ReadTimeout := 1000; end else begin Client1.ConnectTimeout := 500; Client1.ReadTimeout := 2000; end; end else begin Client1.ConnectTimeout := 1000; Client1.ReadTimeout := ReadTimeout; end; HTTPAnswer.Preamble[0] := 0; HTTPAnswer.Preamble[1] := 0; HTTPAnswer.Preamble[2] := 0; HTTPAnswer.Device_ID := 0; HTTPAnswer.Device_Adress := 0; HTTPAnswer.Command := 0; HTTPAnswer.Data1 := 0; HTTPAnswer.Data2 := 0; HTTPAnswer.Data3 := 0; HTTPAnswer.Data4 := 0; HTTPAnswer.Data5 := 0; HTTPAnswer.Data6 := 0; HTTPAnswer.Data7 := 0; HTTPAnswer.Data8 := 0; HTTPAnswer.Data9 := 0; HTTPAnswer.Data10 := 0; try Size.cx := 16; Size.cy := 16; if Client1.Connected = false then Client1.Connect; if Client1.Connected = true then begin Form1.ConnectImage.Bitmap := Form1.IconList.Bitmap(Size, 15); AnswerIsComming := 0; SetLength(t_out, SizeOf(HTTPRequest)); Move(HTTPRequest, t_out[0], SizeOf(HTTPRequest)); Client1.Socket.WriteBufferOpen; Client1.Socket.Write(t_out, SizeOf(HTTPRequest)); Client1.Socket.WriteBufferFlush; Client1.Socket.WriteBufferClose; Client1.Socket.CheckForDataOnSource(100); while Client1.Socket.InputBufferIsEmpty = true do Client1.Socket.CheckForDataOnSource(100); if Client1.Socket.InputBufferIsEmpty = false then begin while HTTPRequest.Command <> Form1.AnswerIsComming do begin Client1.Socket.CheckForDataOnSource(100); TriesToSend := TriesToSend + 1; if TriesToSend < 10 then begin ClusterClient.ChechClient; end else begin Form1.ConnectImage.Bitmap := Form1.IconList.Bitmap(Size, 7); AnswerIsComming := 0; break; end; end; end; end else begin Form1.ConnectImage.Bitmap := Form1.IconList.Bitmap(Size, 7); AnswerIsComming := 0; end; except on E: Exception do begin AnswerIsComming := 0; Form1.ConnectImage.Bitmap := Form1.IconList.Bitmap(Size, 7); end; end; end; Суть в том, что после передачи на сервер команды я жду появления Form1.AnswerIsComming той команды, которую я послал. Пока ее нет, обращаюсь ClusterClient.ChechClient; Работает как-то не стабильно, то получаю ответ, то нет
-
Так вот в этом как раз и вопрос, как мне заставить главный поток ждать ответа от сервера, если ответ приходит не в главном потоке?
-
вот поэтому я и спрашивал. Созрел еще вопрос, ситуация такая, в главном потоке идет запрос на сервер и должна быть обработка ответа, которая приводит к определенным действиям, опять же в главном потоке. Но так как я вынес ответ сервера в поток, то в главном потоке я не вижу реакции. То есть было вот так FillHTTPRequest(0, 0, HTTP_GET_DEVICE_COUNT); if AnswerIsComming = HTTP_GET_DEVICE_COUNT then begin DeviceCount := HTTPAnswer.Data1; end; я делал запрос (FillHTTPRequest), и выдавал ответ (HTTPAnswer), а теперь у меня HTTPAnswer появляется в отдельном потоке. Могу ли я принудительно вызвать обработку потока Execute. То есть примерно вот так FillHTTPRequest(0, 0, HTTP_GET_DEVICE_COUNT); Thread.Execute; if AnswerIsComming = HTTP_GET_DEVICE_COUNT then begin DeviceCount := HTTPAnswer.Data1; end; Но метод Execute - приватный, я могу сделать дополнительный метод для обработки данных на клиенте и просто запускать его в Execute, или это не безопасно?
-
Ясно, спасибо.
-
А можно еще вопрос, у idTCPClient есть свойство Socket и IOHandler, у которых все свойства и методы идентичны. В чем разница между ними? Работает и так и так.
-
Да, интересно. Но для начала сделаю класс потока и переведу все общение клиентов на него. Потом подумаю о синхронизации))
-
Спасибо большое!!! Пошел делать!!!
-
Спасибо большое, очень полезный пример! Сам уже думал о собственном классе. А можно комментарий, я по коду не совсем понял 1. Метод Execute вызывается после создания экземпляра класса и живет, пока экземпляр не будет уничтожен, верно? 2. По методу Execute while not Terminated do begin sleep(10); if MainFrm.MessagingClient.Connected then begin len := 0; try if MainFrm.MessagingClient.IOHandler.CheckForDataOnSource(100) then begin len := MainFrm.MessagingClient.IOHandler.InputBuffer.Size; try MainFrm.MessagingClient.IOHandler.ReadBytes(Buf, len, false); GetMem(pBuf, len); Move(Buf[0], pBuf^, len); ProcessPacket(pBuf); finally SetLength(Buf, 0); end; end; except on E:EIdException do Synchronize(procedure begin MainFrm.MessagingClientDisconnected(nil); end); У Вас клиент MessagingClient лежит на основной форме MainFrm, и Вы обращаетесь к нему из потока, но без синхронизации, это нормально, так работает? Мне казалось, что любые обращения с элементами главного потока должны идти через синхронизацию. if MainFrm.MessagingClient.Connected then И при этом же при в исключении идет синхронизация. Почему так, там обновляются визуальные компоненты? on E:EIdException do Synchronize(procedure begin MainFrm.MessagingClientDisconnected(nil); end);
-
Да, см пост от alexg. Но и в потоке тоже не работает, или я не правильно его применяю
-
Подниму тему. Перехожу с 10.1 на 10.3. Реализован SplashScreeen по методу AlexG, только вместо потоков был банальный Application.ProcessMessages. В 10.3. это перестало работать, но и с потоками не могу совладать. procedure TSplashForm.FormShow(Sender: TObject); begin TThread.Synchronize(TThread.CurrentThread, procedure begin Application.CreateForm(TForm1, Form1); end); //Application.ProcessMessages; TThread.Synchronize(TThread.CurrentThread, procedure begin StartUpLabel.Text := 'Инициализация!!!'; Label1.Text := VERSION; end); end; Не показывает саму форму SplashForm, заставка висит до самого отображения MainForm. Что можно предпринять?
-
подниму тему, может у кого есть идеи есть SplashScreen по SplashScreen.onShow код procedure TSplashForm.FormShow(Sender: TObject); begin Application.CreateForm(TForm1, Form1); StartUpLabel.Text := 'Инициализация!!!'; Label1.Text := VERSION; end; Соответственно я не только не вижу изменений, я вижу, что даже стиль из Stylebook, который лежит на Form1 не применяется. procedure TSplashForm.FormShow(Sender: TObject); begin Application.CreateForm(TForm1, Form1); Application.ProcessMessages; StartUpLabel.Text := 'Инициализация!!!'; Label1.Text := VERSION; Application.ProcessMessages; end; Вот так все прекрасно работает на 10.1 Соответственно вопрос, как это добро переделать под Андроид на 10.3