Delphi-如何确保线程在不丢失内存的情况下正确可靠地终止

Delphi-如何确保线程在不丢失内存的情况下正确可靠地终止,delphi,delphi-10.1-berlin,Delphi,Delphi 10.1 Berlin,情况:我有一个程序,可以启动一个线程在后台连续运行。当我终止这个线程时,我会间歇性地得到一个运行时错误204(无效指针)和内存泄漏 创建线程的代码(它是在对象TJEList中创建的,对象TJEList中有一个方法onlJ2DosynctThreadNotification,在使用线程中的某些TStringList对象执行某些操作的通知中调用该方法): 线程的执行代码: FreeOnTerminate:=True; //I create 5 StringList objects here

情况:我有一个程序,可以启动一个线程在后台连续运行。当我终止这个线程时,我会间歇性地得到一个运行时错误204(无效指针)和内存泄漏

创建线程的代码(它是在对象TJEList中创建的,对象TJEList中有一个方法onlJ2DosynctThreadNotification,在使用线程中的某些TStringList对象执行某些操作的通知中调用该方法):

线程的
执行
代码:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.
为了更好地度量(尽管可能是冗余的),我在线程中还有一个析构函数,它可以:

 if Assigned (TStringList object) then FreeAndNil (TStringList object);
对于创建的所有TStringList对象(然后调用
继承的;

我停止线程的代码是:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.
我调用代码来停止TJEList对象的析构函数中的线程(在应用程序的主窗体关闭时调用该析构函数)

问题: 有时,程序会干净地终止(没有内存泄漏或错误消息)。其他时候,我会收到以下错误消息和内存泄漏(值得一提的是,内存泄漏消息出现在运行时错误消息之前):

我的问题:如何确保线程总是可靠地终止(并因此释放)?任何帮助和/或指导都将不胜感激

香港时间下午5时08分更新20170310:包括要求的MCVE代码

程序代码:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.
主表单代码:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.
我的列表代码:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.
阅读代码:

  FreeOnTerminate:=True;

  //I create 5 StringList objects here - they are declared as private variables in the Thread

  try
    while Not Terminated do
    begin
      //Perform operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //Perform different operation (which internally also checks for Terminated)
      if Terminated then
        Break;
      //etc...
      Sleep (1500);
    end;
  finally
    //I FreeAndNil(...) all the TStringlist objects
  end;
  if Assigned(FLJ2DOSyncThread) then
    if FLJ2DOSyncThread.Started then
      FLJ2DOSyncThread.Terminate;
program ThreadIssueMCVE;

uses
  System.StartUpCopy,
  FMX.Forms,
  frmMain in 'frmMain.pas' {fMain},
  MyList in 'MyList.pas',
  MyThread in 'MyThread.pas';

{$R *.res}

begin
  {$IFDEF DEBUG}
  System.ReportMemoryLeaksOnShutdown:=true;
  {$ENDIF}
  Application.Initialize;
  Application.CreateForm(TfMain, fMain);
  Application.Run;
end.
unit frmMain;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, MyList,
  FMX.Controls.Presentation, FMX.StdCtrls;

type
  TfMain = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    FMyList: TMyList;
  public
    { Public declarations }
  end;

var
  fMain: TfMain;

implementation

{$R *.fmx}

procedure TfMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  FreeAndNil(FMyList);
end;

procedure TfMain.FormCreate(Sender: TObject);
begin
  FMyList:=TMyList.Create;
end;

end.
unit MyList;

interface

uses
  System.Classes, System.SysUtils, MyThread;


type
  TMyList = Class (TObject)
  private
    FSomeList: TStringList;
    FMyThread: TMyThread;
  protected
    procedure OnMyThreadNotification (Sender: TObject);
    procedure OnMyThreadTerminate (Sender: TObject);
    procedure ActOnThreadResults (AList: TStringList);
  public
    procedure InitMyThread;
    procedure StopMyThread;
    constructor Create;
    destructor Destroy; override;
  property
    SomeList: TStringList read FSomeList;
  end;

implementation

{ TMyList }

constructor TMyList.Create;
begin
  inherited Create;

  FSomeList:=TStringList.Create;

  InitMyThread;
end;

destructor TMyList.Destroy;
begin
  StopMyThread;

  FreeAndNil(FSomeList);

  inherited Destroy;
end;

procedure TMyList.ActOnThreadResults (AList: TStringList);
var
  i: Integer;
begin
  for i:= 0 to AList.Count-1 do
  begin
    if FMyThread.CheckTerminated then
      exit;
    FSomeList.Add(AList.Strings[i]);
  end;
end;

procedure TMyList.InitMyThread;
begin
  FMyThread:=TMyThread.Create (True);
  FMyThread.NotifyEvent:=OnMyThreadNotification;
  FMyThread.OnTerminate:=OnMyThreadTerminate;
  FMyThread.Start;
end;

procedure TMyList.OnMyThreadNotification(Sender: TObject);
var
  fullList: TStringList;
begin
  if (FMyThread.FList4.Count>0) or (FMyThread.FList5.Count>0) then
  begin
    fullList:=TStringList.Create;
    try
      fullList.Text:=FMyThread.FList4.Text + FMyThread.FList5.Text;
      ActOnThreadResults(fullList);
    finally
      FreeAndNil (fullList);
    end;
  end;
end;

procedure TMyList.OnMyThreadTerminate(Sender: TObject);
begin
  FreeAndNil(FMyThread);
end;

procedure TMyList.StopMyThread;
begin
  FMyThread.Terminate;
end;

end.
unit MyThread;

interface

uses
  System.Classes, System.SysUtils;

type
  TMyThread = Class (TThread)
  private
    FLastRun: TDateTime;
    FList1: TStringList;
    FList2: TStringList;
    procedure SomeProcess;
    procedure SomeOtherProcess;
  protected
    procedure Execute; override;
  public
    NotifyEvent: TNotifyEvent;
    FList3: TStringList;
    FList4: TStringList;
    FList5: TStringList;
    destructor Destroy; override;
  End;

implementation

destructor TMyThread.Destroy;
begin
  FreeAndNil(FList1);
  FreeAndNil(FList2);
  FreeAndNil(FList3);
  FreeAndNil(FList4);
  FreeAndNil(FList5);

  inherited;
end;

procedure TMyThread.SomeOtherProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList5.Add(i.ToString);
  end;
end;

procedure TMyThread.SomeProcess;
var i: integer;
begin
  for i := 1 to 1000000 do
  begin
    if Terminated then
      break;

    //do some stuff here
    FList4.Add(i.ToString);
  end;
end;


procedure TMyThread.Execute;
var
  boolCheck: Boolean;
begin
  NameThreadForDebugging('Thread with issues');
  FreeOnTerminate:=False;

  FList1:=TStringList.Create;
  FList2:=TStringList.Create;
  FList3:=TStringList.Create;
  FList4:=TStringList.Create;
  FList5:=TStringList.Create;
  FLastRun:=Now; //i get this from an ini file normally

  try
    while Not Terminated do
    begin
      if Terminated then
        Break;
      FList1.Clear;
      FList2.Clear;
      FList3.Clear;
      FList4.Clear;
      FList5.Clear;

      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;
      SomeOtherProcess;
      if Terminated then
        Break;
      SomeProcess;
      if Terminated then
        Break;

      if (FList4.Count>0) OR (FList5.Count>0) then
        boolCheck:=True;

      if Terminated then
        Break;
      if boolCheck then
        NotifyEvent(Self);

      if Terminated then
        Break;
      Sleep (2000);

      if Terminated then
        Break;

      FLastRun:=Now;      //i save to ini file as well
    end;
  finally
    //i save to ini file the last run
    FreeAndNil(FList1);
    FreeAndNil(FList2);
    FreeAndNil(FList3);
    FreeAndNil(FList4);
    FreeAndNil(FList5);
  end;
end;

end.

你有比赛的危险。如果在线程自然终止之前销毁TMyList对象,它仍然可以调用notify事件,但对象不再存在(即使线程仍然存在)。我发现处理此问题和停止内存泄漏的最简单方法是等待线程在StopMyThread例程中终止,并将FMyThread的销毁放入析构函数(目前的情况是,如果退出应用程序,则不会调用terminate)


问题可能与不等待线程完成有关。因此,尝试调用
WaitFor
方法,如下
FLJ2DOSyncThread.WaitFor终止
呼叫后执行code>。@RRUZ感谢您的评论。我考虑过这一点,并尝试添加WaitFor,但当我这样做时,我得到了以下错误:“引发了异常类EThread,消息为‘Thread error:句柄无效(6)”。@Rohit您使用的是
FreeOnTerminate=True
,因此使用
WaitFor()
,这样做是一种可能导致崩溃的竞争条件
FreeOnTerminate
应仅用于启动和忘记线程。这不是那种情况,所以当您使用完线程时,请清除该线程,并显式地释放该线程。问题是您持有对该线程的引用,并将其设置为
FreeOnTerminate
。当thread对象被销毁时,您不会尝试清除该引用。这在雷米的评论中得到了解释。请在拨打免费电话之前停止测试
分配的
Free
已经测试了对象是否已分配,因此您的代码会添加不必要的混乱。为了清晰起见,我做了以下操作:(1)我将
FreeOnTerminate
更改为False。(2) 在StopMyThread代码中,我在线程上调用
Terminate
WaitFor
Free
。这样,无论何时调用这个过程,我都可以确定线程是终止和释放的。再次感谢所有花时间帮助我解决这个(个人而言,相当令人烦恼的)问题的人!