Delphi-如何确保线程在不丢失内存的情况下正确可靠地终止
情况:我有一个程序,可以启动一个线程在后台连续运行。当我终止这个线程时,我会间歇性地得到一个运行时错误204(无效指针)和内存泄漏 创建线程的代码(它是在对象TJEList中创建的,对象TJEList中有一个方法onlJ2DosynctThreadNotification,在使用线程中的某些TStringList对象执行某些操作的通知中调用该方法): 线程的Delphi-如何确保线程在不丢失内存的情况下正确可靠地终止,delphi,delphi-10.1-berlin,Delphi,Delphi 10.1 Berlin,情况:我有一个程序,可以启动一个线程在后台连续运行。当我终止这个线程时,我会间歇性地得到一个运行时错误204(无效指针)和内存泄漏 创建线程的代码(它是在对象TJEList中创建的,对象TJEList中有一个方法onlJ2DosynctThreadNotification,在使用线程中的某些TStringList对象执行某些操作的通知中调用该方法): 线程的执行代码: FreeOnTerminate:=True; //I create 5 StringList objects here
执行代码:
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
。这样,无论何时调用这个过程,我都可以确定线程是终止和释放的。再次感谢所有花时间帮助我解决这个(个人而言,相当令人烦恼的)问题的人!