我一直在编写单元来搜索以指定扩展名结尾的文件,并且能够跳过搜索指定的目录。该数据分别包含在FExtensions
和FIgnorePaths
TStringList
对象中。TStringList.IndexOf()导致线程崩溃
然而,大约1出10次运行时,线程崩溃与以下情况除外:
调试位后,我隔离在搜索线程此线作为碰撞的原因:
if FExtensions.IndexOf(ExtractFileExt(search_rec.Name)) <> -1 then
在我致电IndexOf()
之前,我试过做Assigned(FExtensions)
检查,但没有消除这次事故。如果我评论这一行,线程压力测试可以正常工作(以100ms的间隔创建/销毁它)。我知道TStringList
不是线程安全的,但我不访问FExtensions
,也没有任何其他的TStringList
在任何地方出现它的作用域,所以并发访问不应该是崩溃的原因。
这里是文件搜索线程单位:
unit uFileSearchThread;
interface
uses
Winapi.Windows, System.Classes, System.Generics.Collections;
type
TFileSearchThread = class(TThread)
private
FExternalMessageHandler: HWND;
FMsg_FSTDone : Cardinal;
FPath : String;
FIgnorePaths : TStringList;
FExtensions : TStringList;
FFiles : TStringList;
function IsIgnoreDir(const ADir: String; out AKeepIgnoreCheck: Boolean): Boolean;
protected
procedure Execute; override;
public
constructor Create(const APath: String; const AIgnorePaths: TStringList; const AAllowedExtensions: TStringList; const AExternalMessageHandler: HWND; const AMsg_FSTDone: Cardinal);
destructor Destroy; override;
property Path : String read FPath;
property Files: TStringList read FFiles;
end;
TFileSearchThreads = TObjectList<TFileSearchThread>;
implementation
uses
System.SysUtils, System.StrUtils;
constructor TFileSearchThread.Create(const APath: String; const AIgnorePaths: TStringList; const AAllowedExtensions: TStringList; const AExternalMessageHandler: HWND; const AMsg_FSTDone: Cardinal);
begin
inherited Create(TRUE);
FExternalMessageHandler := AExternalMessageHandler;
FMsg_FSTDone := AMsg_FSTDone;
FPath := IncludeTrailingPathDelimiter(APath);
FIgnorePaths := TStringList.Create;
FIgnorePaths.Assign(AIgnorePaths);
FExtensions := TStringList.Create;
FExtensions.Assign(AAllowedExtensions);
FFiles := TStringList.Create;
WriteLn(FPath, ' file search thread created.');
end;
destructor TFileSearchThread.Destroy;
begin
FExtensions.Free;
FIgnorePaths.Free;
WriteLn(FPath, ' file search thread destroyed.');
inherited;
end;
function TFileSearchThread.IsIgnoreDir(const ADir: String; out AKeepIgnoreCheck: Boolean): Boolean;
var
C1: Integer;
begin
AKeepIgnoreCheck := FALSE;
if not Assigned(FIgnorePaths) then
Exit(FALSE);
for C1 := 0 to FIgnorePaths.Count - 1 do
if AnsiStartsText(FIgnorePaths[C1], ADir) then
Exit(TRUE)
else
if not AKeepIgnoreCheck then
AKeepIgnoreCheck := AnsiStartsText(ADir, FIgnorePaths[C1]);
Exit(FALSE);
end;
procedure TFileSearchThread.Execute;
var
search_rec : TSearchRec;
dirs : TStringList;
dirs_nocheck : TStringList;
dir : String;
ignore_check : Boolean;
ignore_check_tmp: Boolean;
newdir : String;
begin
dirs := TStringList.Create;
try
dirs_nocheck := TStringList.Create;
try
dirs.Add(FPath);
while (not Terminated) and
((dirs.Count > 0) or (dirs_nocheck.Count > 0)) do
begin
ignore_check := dirs.Count > 0;
if ignore_check then
begin
dir := dirs[0];
dirs.Delete(0);
end
else
begin
dir := dirs_nocheck[0];
dirs_nocheck.Delete(0);
end;
if (not ignore_check) or
(not IsIgnoreDir(LowerCase(dir), ignore_check)) then
if FindFirst(dir + '*', faAnyFile, search_rec) = 0 then
try
repeat
if (search_rec.Attr and faDirectory) = 0 then
begin
if FExtensions.IndexOf(ExtractFileExt(search_rec.Name)) <> -1 then // crashes here
FFiles.Add(dir + search_rec.Name);
end
else
if (search_rec.Name <> '.') and (search_rec.Name <> '..') then
begin
newdir := dir + search_rec.Name + '\';
if not ignore_check then
dirs_nocheck.Add(newdir)
else
if not IsIgnoreDir(LowerCase(newdir), ignore_check_tmp) then
if ignore_check_tmp then
dirs.Add(newdir)
else
dirs_nocheck.Add(newdir);
end;
until (Terminated) or (FindNext(search_rec) <> 0);
finally
FindClose(search_rec);
end;
end;
finally
dirs_nocheck.Free;
end;
finally
dirs.Free;
end;
PostMessage(FExternalMessageHandler, FMsg_FSTDone, NativeUInt(pointer(self)), 0);
end;
end.
(我知道我做的析构函数不是免费FFiles,但那是因为我想避免数据重复,所以我线程后,它传递破坏,这样就保持使用它)另一个对象
和程序创建线程:
procedure CreateFileSearchThread(const APath: String);
const
{$I ignore_dirs.inc}
{$I allowed_extensions.inc}
var
ignore_dirs_list, allowed_exts_list: TStringList;
file_search_thread : TFileSearchThread;
C1 : Integer;
begin
ignore_dirs_list := TStringList.Create;
try
ignore_dirs_list.Sorted := TRUE;
ignore_dirs_list.CaseSensitive := FALSE;
ignore_dirs_list.Duplicates := dupIgnore;
for C1 := Low(IGNORE_DIRS) to High(IGNORE_DIRS) do
ignore_dirs_list.Add(LowerCase(ExpandEnvStrings(IGNORE_DIRS[C1])));
allowed_exts_list := TStringList.Create;
try
allowed_exts_list.Sorted := TRUE;
allowed_exts_list.CaseSensitive := FALSE;
allowed_exts_list.Duplicates := dupIgnore;
for C1 := Low(ALLOWED_EXTS) to High(ALLOWED_EXTS) do
allowed_exts_list.Add('.' + ALLOWED_EXTS[C1]);
file_search_thread := TFileSearchThread.Create(APath, ignore_dirs_list, allowed_exts_list, FMessageHandler, FMsg_FSTDone);
FFileSearchThreads.Add(file_search_thread);
file_search_thread.Start;
finally
allowed_exts_list.Free;
end;
finally
ignore_dirs_list.Free;
end;
end;
我摧毁threa d只需调用FFileSearchThreads.Free
,然后应该释放它的对象,因为OwnObjects
设置为TRUE
。 FFileSearchThreads
是TObjectList<TFileSearchThread>
类型。
我相信'TThread.Destroy()'已经做到了这一点? ('Terminate()'然后'WaitFor()')。检查'System.Classes.TThread.Destroy()'析构函数代码。 –
好吧,所以我刚刚尝试过.Terminate()和.WaitFor(),而不是释放对象列表,它的工作原理!但是'TThread'的'.Destroy()'中的相同代码的目的是什么,以及为什么它不以那种方式工作? –
您首先释放您的数据结构,然后调用'inherited Destroy'。此外,根据我的感觉,DTOR不是等待线程终止的正确位置,恕我直言,VCL代码只是为了避免显而易见。但这是我个人的看法。 – JensG