【问题标题】:multi-thread causes memory leak多线程导致内存泄漏
【发布时间】:2018-05-21 14:55:47
【问题描述】:

我创建了一个应用程序,它每 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.

【问题讨论】:

  • 第一个明显的问题是您正在从线程访问 GUI 对象。你不能那样做。这已经在这里讨论了数百次,所以我不会尝试重复建议,只是鼓励您研究该主题。

标签: multithreading delphi indy


【解决方案1】:

在您的线程执行方法中,您正在直接访问 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;

【讨论】:

  • 谢谢。我还注意到内存来自我没有释放SocketOpenSSL: TIdSSLIOHandlerSocketOpenSSL; 我现在已经解决了这个问题,感谢你的例子,我现在了解如何使用 Syncronize
猜你喜欢
  • 2011-09-23
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2022-11-06
  • 2019-10-21
  • 2015-07-06
  • 2014-06-07
  • 2013-11-20
相关资源
最近更新 更多