Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Delphi线程不';跑不动_Delphi_Delphi Xe8_Tthread - Fatal编程技术网

Delphi线程不';跑不动

Delphi线程不';跑不动,delphi,delphi-xe8,tthread,Delphi,Delphi Xe8,Tthread,我正在尝试搜索所有子文件夹中的所有文件,这样会花费很长时间,应用程序会停止响应,所以我使用了Thread(这是第一次使用Threads)我读到了这篇文章,找到了创建和执行线程的方法,但调用线程时什么也没有发生,我不明白为什么我不能在主窗体上使用添加的组件,我必须再次声明它? 我错过了什么 type TSearchThread = class(TThread) private { Private declarations } protected procedure Ex

我正在尝试搜索所有子文件夹中的所有文件,这样会花费很长时间,应用程序会停止响应,所以我使用了
Thread
(这是第一次使用
Threads
)我读到了这篇文章,找到了创建和执行线程的方法,但调用线程时什么也没有发生,我不明白为什么我不能在主窗体上使用添加的组件,我必须再次声明它?
我错过了什么

type
  TSearchThread = class(TThread)
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

procedure AddAllFilesInDir(const Path: string; ListBox:TsListBox);
var
  SR: TSearchRec;
  I: Integer;
begin
  if FindFirst(IncludeTrailingBackslash(Path) + '*.*', faAnyFile or faDirectory, SR) = 0 then
    try
      repeat
        if (SR.Attr and faDirectory) = 0 then
            ListBox.Items.Add(Path+'\'+SR.Name)
        else if (SR.Name <> '.') and (SR.Name <> '..') then
          AddAllFilesInDir(IncludeTrailingBackslash(Path) + SR.Name, ListBox);
          Form1.sPanel2.Caption := Path+'\'+SR.Name;
          Form1.sPanel2.Refresh;
          ListBox.Refresh;
      until FindNext(Sr) <> 0;
    finally
      FindClose(SR);
    end;
end;

procedure TSearchThread.Execute;
var FileList: TsListBox;
    I: Integer;
    {Here I had to re-declare objects}
    sDirectoryEdit1: TsDirectoryEdit;
    sListBox1: TsListBox;
begin
      FileList := TsListBox.Create(nil);
      FileList.Parent := sListBox1;
      FileList.Visible := False;
      AddAllFilesInDir(sDirectoryEdit1.Text+'\', FileList);
      for I := 0 to FileList.Count -1 do
      if sListBox1.Items.IndexOf(FileList.Items.Strings[I]) = -1 then
      sListBox1.Items.Add(FileList.Items.Strings[I]);
      FileList.Clear;
end;


procedure TForm1.sDirectoryEdit1Change(Sender: TObject);
begin
    TSearchThread.Create(False);
end;
类型
tsearchread=class(TThread)
私有的
{私有声明}
受保护的
程序执行;推翻
结束;
过程addAllFileIndir(常量路径:字符串;列表框:TsListBox);
变量
SR:TSearchRec;
I:整数;
开始
如果FindFirst(包括RailingBackslash(路径)+'*.*',faAnyFile或faDirectory,SR)=0,则
尝试
重复
如果(SR.Attr和faDirectory)=0,则
ListBox.Items.Add(路径+'\'+SR.Name)
否则,如果(SR.Name'')和(SR.Name'..'),则
AddAllFileIndir(包括RailingBackslash(路径)+高级名称,列表框);
Form1.sPanel2.Caption:=Path+'\'+SR.Name;
Form1.sPanel2.Refresh;
ListBox.Refresh;
直到FindNext(Sr)0;
最后
FindClose(SR);
结束;
结束;
过程tsearchread.Execute;
var文件列表:TsListBox;
I:整数;
{这里我必须重新声明对象}
sDirectoryEdit1:TsDirectoryEdit;
sListBox1:TsListBox;
开始
FileList:=TsListBox.Create(无);
FileList.Parent:=sListBox1;
FileList.Visible:=False;
AddAllFileIndir(sDirectoryEdit1.Text+'\',文件列表);
对于文件列表,I:=0。计数-1
如果sListBox1.Items.IndexOf(FileList.Items.Strings[I])=-1,则
sListBox1.Items.Add(FileList.Items.Strings[I]);
文件列表。清除;
结束;
程序TForm1.SDirectoryEdit 1更改(发送方:ToObject);
开始
tsearchread.Create(False);
结束;

好的,让我试一试:

首先是线程的新版本:

uses
  IOUtils;

type
  TFileFoundEvent = procedure(const Path: string; const SearchRec: TSearchRec) of object;

  TSearchThread = class(TThread)
  private
    FPath: string;
    FSearchRec: TSearchRec;
    FFileFoundEvent: TFileFoundEvent;
  protected
    procedure Execute; override;
  public
    Constructor Create(const aPath: string; aFileFoundEvent: TFileFoundEvent); reintroduce;
  end;

  { TSearchThread }

constructor TSearchThread.Create(const aPath: string; aFileFoundEvent: TFileFoundEvent);
begin
  // Create the Thread non suspended
  inherited Create(false);

  // Copy parameters to local members.
  FFileFoundEvent := aFileFoundEvent;
  FPath := aPath;

  // Make the sure the thread frees itself after execution
  FreeOnTerminate := True;
end;

procedure TSearchThread.Execute;
var
  FilterPredicate: TDirectory.TFilterPredicate;
begin
  // FilterPredicate is an in-place anonymous method to be called each time the TDirectory.GetFiles finds a file
  FilterPredicate := function(const Path: string; const SearchRec: TSearchRec): Boolean
    begin
      // Since we can not access from within Synchronize we need to copy iot to a member of the class
      FSearchRec := SearchRec;

      // You cannot access VCL objects directly from a thread.
      // So you need to call Syncronize
      // For more info look in the online help
      // http://docwiki.embarcadero.com/Libraries/Seattle/en/System.Classes.TThread.Synchronize
      Synchronize(nil,
        procedure
        begin
          FFileFoundEvent(FPath, FSearchRec);
        end);

      Result := True;
    end;

  // Do the search
  TDirectory.GetFiles(FPath, TSearchOption.soTopDirectoryOnly, FilterPredicate)
end;
主要区别在于我将回调过程传递给线程的构造函数。因为我使用
TDirectory.GetFiles
来搜索文件。在
IOUtils

然后你需要使用它:在from上放置一个列表框,然后像这样调用它:

表格定义:

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
  private
    procedure FileFoundEvent(const Path: string; const SearchRec: TSearchRec);
  public
    { Public declarations }
  end;

如果您不想看到正在进行的搜索结果,而是想加快搜索速度,您可以创建一个版本的searchthread,一次给出所有结果:

uses
  IOUtils;

type
  TSearchThread = class(TThread)
  private
    FSearchPath: String;
    FResultBuffer: TStrings;
  protected
    procedure Execute; override;
  public
    constructor Create(const aSearchPath: string; aResultBuffer: TStrings); overload;
  end;

constructor TSearchThread.Create(const aSearchPath: string; aResultBuffer: TStrings);
begin
  inherited Create(false);
  FSearchPath := IncludeTrailingPathDelimiter(aSearchPath);
  FResultBuffer := aResultBuffer;
  FreeOnTerminate := True;
end;

procedure TSearchThread.Execute;
var
  FBuffer: TStringlist;
  Filename: String;
begin
  Synchronize(nil,
    procedure
    begin
      FResultBuffer.Text := 'Searching ' + FSearchPath;
    end);

  FBuffer := TStringlist.Create;
  for Filename in TDirectory.GetFiles(FSearchPath, TSearchOption.soAllDirectories, nil) do
    FBuffer.Add(Filename);

  Synchronize(nil,
    procedure
    begin
      FResultBuffer.Assign(FBuffer);
    end);

  FreeAndNil(FBuffer);
end;
你必须以不同的方式调用这个线程

表单设置仍然和以前一样:表单上的列表框

type
  TForm1 = class(TForm)
    ListBox1: TListBox;
    procedure FormCreate(Sender: TObject);
  private
    Stopwatch: TStopwatch;
    procedure SearchThreadTerminate(Sender: TObject);
  public
    { Public declarations }
  end;
然后是实施:

procedure TForm1.FormCreate(Sender: TObject);
begin
  Stopwatch := TStopwatch.StartNew;

  with TSearchThread.Create('C:\Program Files (x86)\Embarcadero\', ListBox1.Items) do
    OnTerminate := SearchThreadTerminate;
end;

procedure TForm1.SearchThreadTerminate(Sender: TObject);
begin
  Stopwatch.Stop;
  Caption := 'Elapsed Milliseconds: ' + IntToStr(Stopwatch.ElapsedMilliseconds) + ' Files found: ' + IntToStr(ListBox1.Items.Count);
end;
这个版本的优点是速度快。升级屏幕速度很慢,第一个解决方案会为找到的每个文件更新屏幕,而这个解决方案只会更新屏幕两次


试试看

这里已经讨论过很多次了。无法从线程访问VCL对象。使用
TThread.Queue
TThread.Synchronize
@JensBorrisholt请不要以这种方式纠缠询问者。Asker问了4个问题,包括这一个。一个问题有一个公认的答案。另一个问题没有答案。最后一个问题有答案,但我还不清楚它们是否应该被接受。@Jens也许你犯了一个错误。在过去的几次我试图与你就你的答案进行推理时,你并不热衷于倾听。所以,在我看来,你的答案中有一个你还看不到的缺陷似乎是合理的。上一次我试图与你接触时,你指责我剽窃,还侮辱了我。@Jens:在你可以问一个新问题之前,它在哪里说你必须关闭所有现有的问题?事实上,它在哪里说你必须结束任何问题?@JensBorrisholt:我同意DavidH,你对你回答的人有一种奇怪的、威吓的态度(比如责骂他们说,如果你的“解决方案”对他们不起作用,那是他们的错)。让它休息和/或长大。嗯,如果你不在列表框的项目上调用Begin/EndUpdate,那么在线程中做这些工作有什么意义吗?@MartynA-Um,在搜索过程中保持应用程序的响应?在同步数据之前进行一段短时间的缓冲可以防止主线程在有大量文件时阻塞。@jerrydoge:no。有比这个答案更好(更简单)的方法。假设有100k+个文件要添加…@MartynA正确如果有100k+个文件,它会减慢搜索过程,但GUI会在搜索过程中做出响应。我做了一个只更新两次GUI的更新版本。
procedure TForm1.FormCreate(Sender: TObject);
begin
  Stopwatch := TStopwatch.StartNew;

  with TSearchThread.Create('C:\Program Files (x86)\Embarcadero\', ListBox1.Items) do
    OnTerminate := SearchThreadTerminate;
end;

procedure TForm1.SearchThreadTerminate(Sender: TObject);
begin
  Stopwatch.Stop;
  Caption := 'Elapsed Milliseconds: ' + IntToStr(Stopwatch.ElapsedMilliseconds) + ' Files found: ' + IntToStr(ListBox1.Items.Count);
end;