0

I have an application that has multiple forms, each form has a thread that does a GET() on a given url every 3 seconds ... the problem is that every call of GET() the user interface freezes until the GET() be completed, and how are various forms doing the same thing every 3 seconds, the application is very slow, has put a "IdAntiFreeze" on the main form, most did not work, so read the "IdAntiFreeze" does not work in Threads, follows the example below:

  private
    { Private declarations }
    GlobalHtml : String;
    TimerGlobal : Integer;

//Thread

TMyThread= class(TThread)
private
  FForm : TForm1;
  strTemp: String;
protected
  procedure Execute; override;
public
  constructor Create(Form : TForm1; gpLinkTemp : String);
  destructor Destroy; override;
end;

//get string http

procedure TMyThread.GetStringHttp;
var
  meuIdHTTP : TIdHttp;
  strResponse:  String;
begin
  meuIdHTTP := TIdHTTP.Create(nil);
  strResponse := meuIdHTTP.Get('url...'); //<-- freezes up complete
  FForm.GlobalHtml := strResponse; //<-- private form variable
end;

//execute

procedure TMyThread.Execute;
begin
  while not (terminated) do
   begin 
      Synchronize(GetStringHttp);
      FForm.LabelStatus.Visible := False;
      FForm.ButtonStatus.Enabled := False;
      FForm.TimerStatus.Enabled := True;
      if FForm.TimerGlobal >= 10 then
       begin
         Synchronize(UpdateGrid);
         FForm.TimerGlobal := 0;
       end;
   end;
end;

//update TStringGrid on form

procedure TForm1.UpdateGrid;
begin
 //I update the TStringGrid here with the data of variable
 TSGridDados.Cells[0,1] := GlobalHtml;
 //...
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
 Inc(TimerGlobal);
end;

Call Thread:

procedure TForm1.Button1Click(Sender: TObject);
begin
  TMyThread.Create(Self, 'string-url');
end;

If the internet is slow or the server takes to respond is even worse, is there any special configuration in TIdHttp component to avoid this? Why does it happen? Thanks in advance!

Jason-X
  • 39
  • 2
  • 11
  • 1
    How do you execute your thread? I think you may be calling just `Yourthread.Execute`, in which case you are just calling the method synchronously as if it were any other method. If you would execute the threead in a proper way (using YourThread.Start), you wouldn't experience this issue. If this information doesn't help you, please add the piece of code that calls the thread to your question, so we can check and correct it. – GolezTrol Nov 22 '15 at 22:56
  • what do FForm (TForm1) used for in your thread? Updating GUI (Form1) on execute method of a thread is not safe. Perharps you should provide any remaining codes on TMyThread.Execute for better analizing... – Theo Nov 22 '15 at 23:54
  • 1
    `TIdHTTP.Get()` runs in the context of the thread that is calling it. Given the code you have shown, there is no possible way that calling `Get()` in the context of a worker thread can cause any freezing in the main UI thread. So something else in your project has to be causing the freezes. – Remy Lebeau Nov 23 '15 at 00:34
  • Access some controls to get the values and change the status, as well as a procedure to update the data of a TStringGrid, follows updated complete code, writing of data in TStringGrid is in 'Synchronize()' there's something wrong now? Thank you again! – Jason-X Nov 24 '15 at 01:27
  • Staff, any ideas? I do as I am doing? using idhttp in a procedure of thread and updating data in a grid Syncronize? or TIdHttp have to be within the "Run '? – Jason-X Nov 25 '15 at 11:56
  • I do not know if it would be an answer, but managed to solve using the component "TIdThreadComponent" everything is working perfectly now! – Jason-X Nov 27 '15 at 15:04

1 Answers1

0

Here is an example of using TIDHttp in thread.
You can use multiple thread with TList or TThreadList instead of one thread as provided below.
The main idea is using "thread safe" list for waitinglist and resultlist.
waitinglist used to list every url request while resultlist used to list responses from idhttp.get().

Timer on Form used to pop each item from resultlist for GUI updating purpose.

here is the code:
.dfm:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 396
  ClientWidth = 660
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCloseQuery = FormCloseQuery
  OnCreate = FormCreate
  OnDestroy = FormDestroy
  PixelsPerInch = 96
  TextHeight = 13
  object Splitter1: TSplitter
    Left = 193
    Top = 33
    Height = 363
    ExplicitLeft = 256
    ExplicitTop = 248
    ExplicitHeight = 100
  end
  object TreeView1: TTreeView
    Left = 0
    Top = 33
    Width = 193
    Height = 363
    Align = alLeft
    Indent = 19
    ReadOnly = True
    TabOrder = 0
    OnChange = TreeView1Change
    OnDeletion = TreeView1Deletion
  end
  object Memo1: TMemo
    Left = 196
    Top = 33
    Width = 464
    Height = 363
    Align = alClient
    Lines.Strings = (
      'Memo1')
    ScrollBars = ssBoth
    TabOrder = 1
    WordWrap = False
  end
  object Panel1: TPanel
    Left = 0
    Top = 0
    Width = 660
    Height = 33
    Align = alTop
    BevelOuter = bvNone
    TabOrder = 2
    object Button1: TButton
      Left = 5
      Top = 5
      Width = 75
      Height = 25
      Caption = 'get URL'
      TabOrder = 0
      OnClick = Button1Click
    end
  end
  object IdAntiFreeze1: TIdAntiFreeze
    Left = 56
    Top = 64
  end
  object Timer1: TTimer
    Interval = 33
    OnTimer = Timer1Timer
    Left = 56
    Top = 120
  end
end

.pas:

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdBaseComponent, IdAntiFreezeBase,
  Vcl.IdAntiFreeze, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
  Vcl.ComCtrls, Vcl.ExtCtrls, Vcl.StdCtrls, IdThreadSafe;

type

  TMyUrl = class(TStringlist)
  private
    fUrl:String;
  end;

  TMyThread= class(TThread)
  private
    fWaitingList : TIdThreadSafeObjectList;
    fResultList  : TIdThreadSafeObjectList;
  protected
    procedure Execute; override;
  public
    constructor Create(aWaitingList, aResultList:TIdThreadSafeObjectList);
  end;

  TForm1 = class(TForm)
    IdAntiFreeze1: TIdAntiFreeze;
    TreeView1: TTreeView;
    Memo1: TMemo;
    Splitter1: TSplitter;
    Timer1: TTimer;
    Panel1: TPanel;
    Button1: TButton;
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure TreeView1Deletion(Sender: TObject; Node: TTreeNode);
    procedure Timer1Timer(Sender: TObject);
    procedure TreeView1Change(Sender: TObject; Node: TTreeNode);
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    fWaitingList : TIdThreadSafeObjectList;
    fResultList  : TIdThreadSafeObjectList;
    fThread      : TmyThread;  //... You can change this to Multiple Threads instead
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{TMyThread}
constructor TMyThread.Create(aWaitingList, aResultList:TIdThreadSafeObjectList);
begin
  inherited Create;
  fWaitingList:=aWaitingList;
  fResultList:=aResultList;
end;

procedure TMyThread.Execute;
var
  Url:TMyUrl;
  http:TIdHttp;
begin
  while not Terminated do begin
    url:=fWaitingList.Pop;
    if assigned(url) then begin
      http:=TIdHTTP.Create(nil);
      try
        try
          url.Text:=http.Get(Url.fUrl);
        except
          on E:exception do
            Url.Text:=E.Message;
        end;
      finally
        http.Free;
        fResultList.Add(Url);
      end;
    end else
      sleep(100);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  MyUrl:TMyUrl;
  url:String;
begin
  url:='http://';
  if InputQuery('Add Url','Set Url',url) then begin
    url:=trim(url);
    if (sametext(copy(url,1,7),'http://')) and (length(url)>7) then begin
       MyUrl:=TMyUrl.Create;
       MyUrl.fUrl:=url;
       fWaitingList.Add(MyUrl);
    end;
  end;
end;

procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  fThread.Terminate;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  fWaitingList:=TIdThreadSafeObjectList.Create;
  fResultList:=TIdThreadSafeObjectList.Create;
  fWaitingList.OwnsObjects:=true;
  fResultList.OwnsObjects:=true;
  fThread:=TMyThread.Create(fWaitingList, fResultList);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FreeAndNil(fWaitingList);
  FreeAndNil(fResultList);
  FreeAndNil(fThread);
end;

//.... For updating GUI every 33 miliseconds
procedure TForm1.Timer1Timer(Sender: TObject);
var
  url:TMyUrl;
begin
  url:=fResultList.Pop;
  if assigned(url) then begin
    with TreeView1.Items.AddChild(nil,url.fUrl) do
      data:=url;
  end;
end;

procedure TForm1.TreeView1Change(Sender: TObject; Node: TTreeNode);
begin
  if TreeView1.Selected<>nil then
    Memo1.Text:=TMyUrl(TreeView1.Selected.Data).Text;
end;

procedure TForm1.TreeView1Deletion(Sender: TObject; Node: TTreeNode);
begin
  TObject(Node.Data).Free;
  Node.Data:=nil;
end;

end.

Hope this small example could give some inspirations for your works.

Theo
  • 454
  • 1
  • 7
  • 21