I'm made an application that receives data from an API every 10 seconds and this is all done within 2 threads. I can't figure out why there is a memory leak as I free all that I used after the task is done.
I want to apologize if I'm missing or doing something wrong as I'm new to threading and I don't fully understand how it works.
I tried to remake this: Synchronizing Threads and GUI in a Delphi Application but failed as I don't understand what is going on. If someone can please explain to me what I'm doing wrong and how I can fix it or make it better.
Current Code:
unit uMain;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls,
IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP,
IdSSLOpenSSL, IdIOHandler, IdIOHandlerSocket, IdIOHandlerStack, IdSSL;
type
TfrmMain = class(TForm)
grpLuno: TGroupBox;
lblBid: TLabel;
lblRolling24HourVolume: TLabel;
grpBinance: TGroupBox;
tmrRefresh: TTimer;
lblPrice: TLabel;
lblBinanceVolume: TLabel;
lbl1: TLabel;
lbl24hChange: TLabel;
procedure Refresh;
procedure tmrRefreshTimer(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TGetLuno = class(TThread)
protected
procedure Execute; override;
end;
type
TGetBinance = class(TThread)
protected
procedure Execute; override;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
uses
djson, DateUtils, Math;
{ TForm1 }
procedure TfrmMain.FormCreate(Sender: TObject);
begin
SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NoMove or SWP_NoSize);
Refresh;
end;
procedure TfrmMain.Refresh;
begin
with TGetLuno.Create do
begin
FreeOnTerminate := True;
end;
with TGetBinance.Create do
begin
FreeOnTerminate := True;
end;
end;
procedure TfrmMain.tmrRefreshTimer(Sender: TObject);
begin
Refresh;
end;
{ TGetLuno }
procedure TGetLuno.Execute;
var
httpclient: TIdHTTP;
sdata: string;
jdata: TJSON;
begin
httpclient := TIdHTTP.Create(nil);
try
sdata := httpclient.Get('https://api.mybitx.com/api/1/ticker?pair=XBTZAR');
finally
httpclient.Free;
end;
jdata := TJSON.Parse(sdata);
try
frmMain.lblBid.Caption := 'Price: R ' + jdata['bid'].AsString;
frmMain.lblRolling24HourVolume.Caption := 'Volume: ' + jdata['rolling_24_hour_volume'].AsString;
finally
jdata.Free;
end;
end;
{ TGetBinance }
procedure TGetBinance.Execute;
var
httpclient: TIdHTTP;
sdata: string;
jdata: TJSON;
SocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
httpclient := TIdHTTP.Create(nil);
SocketOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SocketOpenSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
httpclient.IOHandler := SocketOpenSSL;
try
sdata := httpclient.Get('https://api.binance.com/api/v1/ticker/24hr?symbol=BTCUSDT');
finally
httpclient.Free;
end;
jdata := TJSON.Parse(sdata);
try
frmMain.lblPrice.Caption := 'Price: $ ' + jdata['lastPrice'].AsString;
frmMain.lblBinanceVolume.Caption := 'Volume: ' + jdata['volume'].AsString;
if StrToFloat(StringReplace(jdata['priceChangePercent'].AsString, '.', ',', [rfReplaceAll, rfIgnoreCase])) > 0 then
frmMain.lbl24hChange.Font.Color := clLime
else
frmMain.lbl24hChange.Font.Color := clRed;
frmMain.lbl24hChange.Caption := StringReplace(FloatToStr(RoundTo(StrToFloat(StringReplace(jdata['priceChangePercent'].AsString, '.', ',', [rfReplaceAll, rfIgnoreCase])), -2)), ',', '.', [rfReplaceAll, rfIgnoreCase]) + ' %';
finally
jdata.Free;
end;
end;
initialization
ReportMemoryLeaksOnShutdown := True;
end.
Inside your thread execute methods, you are accessing VCL frmMain
components directly.
Since the VCL framework must be executed in the main thread only, you will need to divert those calls. Use TThread.Synchronize() or TThread.Queue() for example:
jdata := TJSON.Parse(sdata);
try
Synchronize(
procedure
begin
frmMain.lblPrice.Caption := 'Price: $ ' + jdata['lastPrice'].AsString;
frmMain.lblBinanceVolume.Caption := 'Volume: ' + jdata['volume'].AsString;
if StrToFloat(StringReplace(jdata['priceChangePercent'].AsString, '.',
',', [rfReplaceAll, rfIgnoreCase])) > 0 then
frmMain.lbl24hChange.Font.Color := clLime
else
frmMain.lbl24hChange.Font.Color := clRed;
frmMain.lbl24hChange.Caption :=
StringReplace(FloatToStr(RoundTo(StrToFloat(StringReplace(jdata
['priceChangePercent'].AsString, '.', ',', [rfReplaceAll, rfIgnoreCase])
), -2)), ',', '.', [rfReplaceAll, rfIgnoreCase]) + ' %';
end);
finally
jdata.Free;
end;