多线程导致内存泄漏

时间:2018-05-21 14:55:47

标签: multithreading delphi indy

我做了一个应用程序,每隔10秒从API接收数据,这都是在2个线程内完成的。我无法弄清楚为什么会出现内存泄漏,因为我在任务完成后释放了我所使用的所有东西。

如果我因为线程新手而错过或做错事我想道歉,而且我不完全理解它是如何工作的。

我试图重拍这个:Synchronizing Threads and GUI in a Delphi Application但失败了,因为我不明白发生了什么。如果有人能够向我解释我做错了什么以及如何解决或改善它。

当前代码:

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.

1 个答案:

答案 0 :(得分:3)

在线程执行方法中,您直接访问VCL frmMain组件。

由于VCL框架只能在主线程中执行,因此您需要转移这些调用。例如,使用TThread.Synchronize()TThread.Queue()

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;