使用线程复制主线程添加到字符串列表的文件

发布于 2024-08-17 18:35:51 字数 5890 浏览 15 评论 0原文

我有一个网络创建程序,在构建网站时会创建数百个文件。

当互联网根文件夹位于本地电脑上时,程序运行正常。如果互联网根文件夹位于网络驱动器上,则复制创建的页面比创建页面本身花费的时间更长(页面的创建已相当优化)。

我正在考虑在本地创建文件,将创建的文件的名称添加到 TStringList 中,并让另一个线程将它们复制到网络驱动器(从 TStringList 中删除复制的文件)。

然而,我以前从未使用过线程,并且在涉及线程的其他 Delphi 问题中找不到现有答案(如果我们可以在搜索字段中使用 and 运算符< /em>),所以我现在问是否有人有一个可以执行此操作的工作示例(或者可以向我指出一些包含工作Delphi代码的文章)?

我正在使用 Delphi 7。

编辑:我的示例项目(感谢 mghie 的原始代码 - 在此再次感谢他)。

  ...
  fct : TFileCopyThread;
  ...

  procedure TfrmMain.FormCreate(Sender: TObject);
  begin
     if not DirectoryExists(DEST_FOLDER)
     then
        MkDir(DEST_FOLDER);
     fct := TFileCopyThread.Create(Handle, DEST_FOLDER);
  end;


  procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
  begin
     FreeAndNil(fct);
  end;

  procedure TfrmMain.btnOpenClick(Sender: TObject);
  var sDir : string;
      Fldr : TedlFolderRtns;
      i : integer;
  begin
     if PickFolder(sDir,'')
     then begin
        // one of my components, returning a filelist [non threaded  :) ] 
        Fldr := TedlFolderRtns.Create();
        Fldr.FileList(sDir,'*.*',True);
        for i := 0 to Fldr.TotalFileCnt -1 do
        begin
           fct.AddFile( fldr.ResultList[i]);
        end;
     end;
  end;

  procedure TfrmMain.wmFileBeingCopied(var Msg: Tmessage);
  var s : string;
  begin
     s := fct.FileBeingCopied;
     if s <> ''
     then
        lbxFiles.Items.Add(fct.FileBeingCopied);
     lblFileCount.Caption := IntToStr( fct.FileCount );
  end;

和单元

  unit eFileCopyThread;
  interface
  uses
     SysUtils, Classes, SyncObjs, Windows, Messages;
  const
    umFileBeingCopied = WM_USER + 1;
  type

    TFileCopyThread = class(TThread)
    private
      fCS: TCriticalSection;
      fDestDir: string;
      fSrcFiles: TStrings;
      fFilesEvent: TEvent;
      fShutdownEvent: TEvent;
      fFileBeingCopied: string;
      fMainWindowHandle: HWND;
      fFileCount: Integer;
      function GetFileBeingCopied: string;
    protected
      procedure Execute; override;
    public
      constructor Create(const MainWindowHandle:HWND; const ADestDir: string);
      destructor Destroy; override;

      procedure AddFile(const ASrcFileName: string);
      function IsCopyingFiles: boolean;
      property FileBeingCopied: string read GetFileBeingCopied;
      property FileCount: Integer read fFileCount;
    end;

  implementation
  constructor TFileCopyThread.Create(const MainWindowHandle:HWND;const ADestDir: string);
  begin
    inherited Create(True);
    fMainWindowHandle := MainWindowHandle;
    fCS := TCriticalSection.Create;
    fDestDir := IncludeTrailingBackslash(ADestDir);
    fSrcFiles := TStringList.Create; 
    fFilesEvent := TEvent.Create(nil, True, False, ''); 
    fShutdownEvent := TEvent.Create(nil, True, False, ''); 
    Resume; 
  end; 

  destructor TFileCopyThread.Destroy; 
  begin 
    if fShutdownEvent <> nil then 
      fShutdownEvent.SetEvent; 
    Terminate;
    WaitFor;
    FreeAndNil(fFilesEvent);
    FreeAndNil(fShutdownEvent);
    FreeAndNil(fSrcFiles);
    FreeAndNil(fCS);
    inherited;
  end;

  procedure TFileCopyThread.AddFile(const ASrcFileName: string);
  begin
    if ASrcFileName <> ''
    then begin
      fCS.Acquire;
      try
        fSrcFiles.Add(ASrcFileName);
        fFileCount := fSrcFiles.Count;
        fFilesEvent.SetEvent;
      finally
        fCS.Release;
      end;
    end;
  end;

  procedure TFileCopyThread.Execute;
  var
    Handles: array[0..1] of THandle;
    Res: Cardinal;
    SrcFileName, DestFileName: string;
  begin
    Handles[0] := fFilesEvent.Handle;
    Handles[1] := fShutdownEvent.Handle;
    while not Terminated do
    begin
      Res := WaitForMultipleObjects(2, @Handles[0], False, INFINITE);
      if Res = WAIT_OBJECT_0 + 1 then
        break;
      if Res = WAIT_OBJECT_0
      then begin
        while not Terminated do
        begin
          fCS.Acquire;
          try
            if fSrcFiles.Count > 0
            then begin
              SrcFileName := fSrcFiles[0];
              fSrcFiles.Delete(0);
              fFileCount := fSrcFiles.Count;
              PostMessage( fMainWindowHandle,umFileBeingCopied,0,0 );
           end else
               SrcFileName := '';
           fFileBeingCopied := SrcFileName;
            if SrcFileName = '' then
              fFilesEvent.ResetEvent;
          finally
            fCS.Release;
          end;

          if SrcFileName = '' then
            break;
          DestFileName := fDestDir + ExtractFileName(SrcFileName);
          CopyFile(PChar(SrcFileName), PChar(DestFileName), True);
        end;
      end;
    end;
  end;

  function TFileCopyThread.IsCopyingFiles: boolean;
  begin 
    fCS.Acquire; 
    try 
      Result := (fSrcFiles.Count > 0) 
        // last file is still being copied 
        or (WaitForSingleObject(fFilesEvent.Handle, 0) = WAIT_OBJECT_0); 
    finally 
      fCS.Release; 
    end; 
  end; 

  // new version - edited after receiving comments 
  function TFileCopyThread.GetFileBeingCopied: string; 
  begin 
     fCS.Acquire; 
     try 
        Result := fFileBeingCopied; 
     finally 
        fCS.Release; 
     end; 
  end; 

  // old version - deleted after receiving comments 
  //function TFileCopyThread.GetFileBeingCopied: string;
  //begin
  //  Result := '';
  //  if fFileBeingCopied <> ''
  //  then begin
  //    fCS.Acquire;
  //    try
  //      Result := fFileBeingCopied;
  //      fFilesEvent.SetEvent;
  //    finally
  //      fCS.Release;
  //    end;
  //  end;
  //end;

  end.

任何额外的评论将不胜感激。

阅读评论并查看示例,您会发现解决方案的不同方法,以及对所有这些方法的赞成和反对意见。

当尝试实现一个复杂的新功能(就像线程对我来说)时的问题是,你几乎总是会找到一些似乎有效的东西......一开始。只是后来你才发现,事情本来应该以不同的方式做。线程就是一个很好的例子。

像 StackOverflow 这样的网站都很棒。真是一个社区啊。

I have a web creation program which, when building a site, creates hundreds of files.

When the internet root folder is situated on the local pc, the program runs fine. If the internet root folder is situated on a network drive, the copying of a created page takes longer than creating the page itself (the creation of the page is fairly optimized).

I was thinking of creating the files locally, adding the names of the created files to a TStringList and let another thread copy them to the network drive (removing the copied file from the TStringList).

Howerver, I have never, ever used threads before and I couldn't find an existing answer in the other Delphi questions involving threads (if only we could use an and operator in the search field), so I am now asking if anyone has got a working example which does this (or can point me to some article with working Delphi code) ?

I am using Delphi 7.

EDITED: My sample project (thx to the original code by mghie - who is hereby thanked once again).

  ...
  fct : TFileCopyThread;
  ...

  procedure TfrmMain.FormCreate(Sender: TObject);
  begin
     if not DirectoryExists(DEST_FOLDER)
     then
        MkDir(DEST_FOLDER);
     fct := TFileCopyThread.Create(Handle, DEST_FOLDER);
  end;


  procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
  begin
     FreeAndNil(fct);
  end;

  procedure TfrmMain.btnOpenClick(Sender: TObject);
  var sDir : string;
      Fldr : TedlFolderRtns;
      i : integer;
  begin
     if PickFolder(sDir,'')
     then begin
        // one of my components, returning a filelist [non threaded  :) ] 
        Fldr := TedlFolderRtns.Create();
        Fldr.FileList(sDir,'*.*',True);
        for i := 0 to Fldr.TotalFileCnt -1 do
        begin
           fct.AddFile( fldr.ResultList[i]);
        end;
     end;
  end;

  procedure TfrmMain.wmFileBeingCopied(var Msg: Tmessage);
  var s : string;
  begin
     s := fct.FileBeingCopied;
     if s <> ''
     then
        lbxFiles.Items.Add(fct.FileBeingCopied);
     lblFileCount.Caption := IntToStr( fct.FileCount );
  end;

and the unit

  unit eFileCopyThread;
  interface
  uses
     SysUtils, Classes, SyncObjs, Windows, Messages;
  const
    umFileBeingCopied = WM_USER + 1;
  type

    TFileCopyThread = class(TThread)
    private
      fCS: TCriticalSection;
      fDestDir: string;
      fSrcFiles: TStrings;
      fFilesEvent: TEvent;
      fShutdownEvent: TEvent;
      fFileBeingCopied: string;
      fMainWindowHandle: HWND;
      fFileCount: Integer;
      function GetFileBeingCopied: string;
    protected
      procedure Execute; override;
    public
      constructor Create(const MainWindowHandle:HWND; const ADestDir: string);
      destructor Destroy; override;

      procedure AddFile(const ASrcFileName: string);
      function IsCopyingFiles: boolean;
      property FileBeingCopied: string read GetFileBeingCopied;
      property FileCount: Integer read fFileCount;
    end;

  implementation
  constructor TFileCopyThread.Create(const MainWindowHandle:HWND;const ADestDir: string);
  begin
    inherited Create(True);
    fMainWindowHandle := MainWindowHandle;
    fCS := TCriticalSection.Create;
    fDestDir := IncludeTrailingBackslash(ADestDir);
    fSrcFiles := TStringList.Create; 
    fFilesEvent := TEvent.Create(nil, True, False, ''); 
    fShutdownEvent := TEvent.Create(nil, True, False, ''); 
    Resume; 
  end; 

  destructor TFileCopyThread.Destroy; 
  begin 
    if fShutdownEvent <> nil then 
      fShutdownEvent.SetEvent; 
    Terminate;
    WaitFor;
    FreeAndNil(fFilesEvent);
    FreeAndNil(fShutdownEvent);
    FreeAndNil(fSrcFiles);
    FreeAndNil(fCS);
    inherited;
  end;

  procedure TFileCopyThread.AddFile(const ASrcFileName: string);
  begin
    if ASrcFileName <> ''
    then begin
      fCS.Acquire;
      try
        fSrcFiles.Add(ASrcFileName);
        fFileCount := fSrcFiles.Count;
        fFilesEvent.SetEvent;
      finally
        fCS.Release;
      end;
    end;
  end;

  procedure TFileCopyThread.Execute;
  var
    Handles: array[0..1] of THandle;
    Res: Cardinal;
    SrcFileName, DestFileName: string;
  begin
    Handles[0] := fFilesEvent.Handle;
    Handles[1] := fShutdownEvent.Handle;
    while not Terminated do
    begin
      Res := WaitForMultipleObjects(2, @Handles[0], False, INFINITE);
      if Res = WAIT_OBJECT_0 + 1 then
        break;
      if Res = WAIT_OBJECT_0
      then begin
        while not Terminated do
        begin
          fCS.Acquire;
          try
            if fSrcFiles.Count > 0
            then begin
              SrcFileName := fSrcFiles[0];
              fSrcFiles.Delete(0);
              fFileCount := fSrcFiles.Count;
              PostMessage( fMainWindowHandle,umFileBeingCopied,0,0 );
           end else
               SrcFileName := '';
           fFileBeingCopied := SrcFileName;
            if SrcFileName = '' then
              fFilesEvent.ResetEvent;
          finally
            fCS.Release;
          end;

          if SrcFileName = '' then
            break;
          DestFileName := fDestDir + ExtractFileName(SrcFileName);
          CopyFile(PChar(SrcFileName), PChar(DestFileName), True);
        end;
      end;
    end;
  end;

  function TFileCopyThread.IsCopyingFiles: boolean;
  begin 
    fCS.Acquire; 
    try 
      Result := (fSrcFiles.Count > 0) 
        // last file is still being copied 
        or (WaitForSingleObject(fFilesEvent.Handle, 0) = WAIT_OBJECT_0); 
    finally 
      fCS.Release; 
    end; 
  end; 

  // new version - edited after receiving comments 
  function TFileCopyThread.GetFileBeingCopied: string; 
  begin 
     fCS.Acquire; 
     try 
        Result := fFileBeingCopied; 
     finally 
        fCS.Release; 
     end; 
  end; 

  // old version - deleted after receiving comments 
  //function TFileCopyThread.GetFileBeingCopied: string;
  //begin
  //  Result := '';
  //  if fFileBeingCopied <> ''
  //  then begin
  //    fCS.Acquire;
  //    try
  //      Result := fFileBeingCopied;
  //      fFilesEvent.SetEvent;
  //    finally
  //      fCS.Release;
  //    end;
  //  end;
  //end;

  end.

Any additional comments would be much appreciated.

Reading the comments and looking at the examples, you find different approaches to the solutions, with pro and con comments on all of them.

The problem when trying to implement a complicated new feature (as threads are to me), is that you almost always find something which seems to work ... at first. Only later on you find out the hard way that things should have been done differently. And threads are a very good example of this.

Sites like StackOverflow are great. What a community.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

打小就很酷 2024-08-24 18:35:51

一个快速而肮脏的解决方案:

type
  TFileCopyThread = class(TThread)
  private
    fCS: TCriticalSection;
    fDestDir: string;
    fSrcFiles: TStrings;
    fFilesEvent: TEvent;
    fShutdownEvent: TEvent;
  protected
    procedure Execute; override;
  public
    constructor Create(const ADestDir: string);
    destructor Destroy; override;

    procedure AddFile(const ASrcFileName: string);
    function IsCopyingFiles: boolean;
  end;

constructor TFileCopyThread.Create(const ADestDir: string);
begin
  inherited Create(True);
  fCS := TCriticalSection.Create;
  fDestDir := IncludeTrailingBackslash(ADestDir);
  fSrcFiles := TStringList.Create;
  fFilesEvent := TEvent.Create(nil, True, False, '');
  fShutdownEvent := TEvent.Create(nil, True, False, '');
  Resume;
end;

destructor TFileCopyThread.Destroy;
begin
  if fShutdownEvent <> nil then
    fShutdownEvent.SetEvent;
  Terminate;
  WaitFor;
  FreeAndNil(fFilesEvent);
  FreeAndNil(fShutdownEvent);
  FreeAndNil(fSrcFiles);
  FreeAndNil(fCS);
  inherited;
end;

procedure TFileCopyThread.AddFile(const ASrcFileName: string);
begin
  if ASrcFileName <> '' then begin
    fCS.Acquire;
    try
      fSrcFiles.Add(ASrcFileName);
      fFilesEvent.SetEvent;
    finally
      fCS.Release;
    end;
  end;
end;

procedure TFileCopyThread.Execute;
var
  Handles: array[0..1] of THandle;
  Res: Cardinal;
  SrcFileName, DestFileName: string;
begin
  Handles[0] := fFilesEvent.Handle;
  Handles[1] := fShutdownEvent.Handle;
  while not Terminated do begin
    Res := WaitForMultipleObjects(2, @Handles[0], False, INFINITE);
    if Res = WAIT_OBJECT_0 + 1 then
      break;
    if Res = WAIT_OBJECT_0 then begin
      while not Terminated do begin
        fCS.Acquire;
        try
          if fSrcFiles.Count > 0 then begin
            SrcFileName := fSrcFiles[0];
            fSrcFiles.Delete(0);
          end else
            SrcFileName := '';
          if SrcFileName = '' then
            fFilesEvent.ResetEvent;
        finally
          fCS.Release;
        end;

        if SrcFileName = '' then
          break;
        DestFileName := fDestDir + ExtractFileName(SrcFileName);
        CopyFile(PChar(SrcFileName), PChar(DestFileName), True);
      end;
    end;
  end;
end;

function TFileCopyThread.IsCopyingFiles: boolean;
begin
  fCS.Acquire;
  try
    Result := (fSrcFiles.Count > 0)
      // last file is still being copied
      or (WaitForSingleObject(fFilesEvent.Handle, 0) = WAIT_OBJECT_0);
  finally
    fCS.Release;
  end;
end;

要在生产代码中使用它,您需要添加错误处理,也许一些进度通知,并且复制本身可能应该以不同的方式实现,但这应该让您开始。

回答您的问题:

我应该在主程序的 FormCreate 中创建 FileCopyThread (并让它运行),这会以某种方式减慢程序吗?

您可以创建线程,它将阻塞事件并消耗 0 CPU 周期,直到您添加要复制的文件。一旦所有文件都被复制,线程将再次阻塞,因此除了消耗一些内存之外,在程序的整个运行时保持它不会产生负面影响。

我可以向 FileCopyThread 添加普通事件通知(以便我可以发送一个事件,如属性 onProgress:TProgressEvent read fOnProgressEvent write fOnProgressEvent; with fi 列表中的当前文件数以及当前处理的文件。我会喜欢在添加时以及复制例程之前和之后调用此函数

您可以添加通知,但要使它们真正有用,它们需要在主线程的上下文中执行,最简单和最丑陋的方法是将它们用 < 包装。 code>Synchronize() 方法。查看 Delphi Threads 演示,了解如何执行此操作的示例,然后阅读通过在 SO 上搜索“[delphi] 同步”找到的一些问题和答案,了解如何操作。这种技术有很多缺点,

但是,如果您只想显示进度,则无需在每个文件中更新它,而且您已经在 VCL 线程中拥有了所有必要的信息。在添加要复制的文件的位置。您可以简单地以 100 的间隔启动计时器,并让计时器事件处理程序检查线程是否仍然繁忙,以及还有多少文件需要复制。当线程再次被阻塞时,您可以禁用计时器。如果您需要来自线程的更多或不同的信息,那么您可以轻松地向线程类添加更多线程安全方法(例如返回挂起文件的数量)。我从一个最小的界面开始,让事情变得小而简单,仅将其用作灵感。

评论您更新的问题:

您有这段代码:

function TFileCopyThread.GetFileBeingCopied: string;
begin
  Result := '';
  if fFileBeingCopied <> '' then begin
    fCS.Acquire;
    try
      Result := fFileBeingCopied;
      fFilesEvent.SetEvent;
    finally
      fCS.Release;
    end;
  end;
end;

但它有两个问题。首先,所有对数据字段的访问都需要受到保护以确保安全,然后您只是读取数据,而不是添加新文件,因此无需设置事件。修改后的方法很简单:

function TFileCopyThread.GetFileBeingCopied: string;
begin
  fCS.Acquire;
  try
    Result := fFileBeingCopied;
  finally
    fCS.Release;
  end;
end;

您也只设置 fFileBeingCopied 字段,但从不重置它,因此即使线程被阻塞,它也始终等于最后复制的文件。当复制最后一个文件时,您应该将该字符串设置为空,当然,在获取关键部分时也应该这样做。只需将赋值移过 if 块即可。

A quick and dirty solution:

type
  TFileCopyThread = class(TThread)
  private
    fCS: TCriticalSection;
    fDestDir: string;
    fSrcFiles: TStrings;
    fFilesEvent: TEvent;
    fShutdownEvent: TEvent;
  protected
    procedure Execute; override;
  public
    constructor Create(const ADestDir: string);
    destructor Destroy; override;

    procedure AddFile(const ASrcFileName: string);
    function IsCopyingFiles: boolean;
  end;

constructor TFileCopyThread.Create(const ADestDir: string);
begin
  inherited Create(True);
  fCS := TCriticalSection.Create;
  fDestDir := IncludeTrailingBackslash(ADestDir);
  fSrcFiles := TStringList.Create;
  fFilesEvent := TEvent.Create(nil, True, False, '');
  fShutdownEvent := TEvent.Create(nil, True, False, '');
  Resume;
end;

destructor TFileCopyThread.Destroy;
begin
  if fShutdownEvent <> nil then
    fShutdownEvent.SetEvent;
  Terminate;
  WaitFor;
  FreeAndNil(fFilesEvent);
  FreeAndNil(fShutdownEvent);
  FreeAndNil(fSrcFiles);
  FreeAndNil(fCS);
  inherited;
end;

procedure TFileCopyThread.AddFile(const ASrcFileName: string);
begin
  if ASrcFileName <> '' then begin
    fCS.Acquire;
    try
      fSrcFiles.Add(ASrcFileName);
      fFilesEvent.SetEvent;
    finally
      fCS.Release;
    end;
  end;
end;

procedure TFileCopyThread.Execute;
var
  Handles: array[0..1] of THandle;
  Res: Cardinal;
  SrcFileName, DestFileName: string;
begin
  Handles[0] := fFilesEvent.Handle;
  Handles[1] := fShutdownEvent.Handle;
  while not Terminated do begin
    Res := WaitForMultipleObjects(2, @Handles[0], False, INFINITE);
    if Res = WAIT_OBJECT_0 + 1 then
      break;
    if Res = WAIT_OBJECT_0 then begin
      while not Terminated do begin
        fCS.Acquire;
        try
          if fSrcFiles.Count > 0 then begin
            SrcFileName := fSrcFiles[0];
            fSrcFiles.Delete(0);
          end else
            SrcFileName := '';
          if SrcFileName = '' then
            fFilesEvent.ResetEvent;
        finally
          fCS.Release;
        end;

        if SrcFileName = '' then
          break;
        DestFileName := fDestDir + ExtractFileName(SrcFileName);
        CopyFile(PChar(SrcFileName), PChar(DestFileName), True);
      end;
    end;
  end;
end;

function TFileCopyThread.IsCopyingFiles: boolean;
begin
  fCS.Acquire;
  try
    Result := (fSrcFiles.Count > 0)
      // last file is still being copied
      or (WaitForSingleObject(fFilesEvent.Handle, 0) = WAIT_OBJECT_0);
  finally
    fCS.Release;
  end;
end;

To use this in production code you would need to add error handling, maybe some progress notifications, and the copying itself should probably be implemented differently, but this should get you started.

In answer to your questions:

should I create the FileCopyThread in the FormCreate of the main program (and let it running), will that slow down the program somehow ?

You can create the thread, it will block on the events and consume 0 CPU cycles until you add a file to be copied. Once all files have been copied the thread will block again, so keeping it over the whole runtime of the program has no negative effect apart from consuming some memory.

Can I add normal event notification to the FileCopyThread (so that I can send an event as in property onProgress:TProgressEvent read fOnProgressEvent write fOnProgressEvent; with f.i. the current number of files in the list, and the file currently processed. I would like to call this when adding and before and after the copy routine

You can add notifications, but for them to be really useful they need to be executed in the context of the main thread. The easiest and ugliest way to do that is to wrap them with the Synchronize() method. Look at the Delphi Threads demo for an example how to do this. Then read some of the questions and answers found by searching for "[delphi] synchronize" here on SO, to see how this technique has quite a few drawbacks.

However, I wouldn't implement notifications in this way. If you just want to display progress it's unnecessary to update this with each file. Also, you have all the necessary information in the VCL thread already, in the place where you add the files to be copied. You could simply start a timer with an Interval of say 100, and have the timer event handler check whether the thread is still busy, and how many files are left to be copied. When the thread is blocked again you can disable the timer. If you need more or different information from the thread, then you could easily add more thread-safe methods to the thread class (for example return the number of pending files). I started with a minimal interface to keep things small and easy, use it as inspiration only.

Comment on your updated question:

You have this code:

function TFileCopyThread.GetFileBeingCopied: string;
begin
  Result := '';
  if fFileBeingCopied <> '' then begin
    fCS.Acquire;
    try
      Result := fFileBeingCopied;
      fFilesEvent.SetEvent;
    finally
      fCS.Release;
    end;
  end;
end;

but there are two problems with it. First, all access to data fields needs to be protected to be safe, and then you are just reading data, not adding a new file, so there's no need to set the event. The revised method would simply be:

function TFileCopyThread.GetFileBeingCopied: string;
begin
  fCS.Acquire;
  try
    Result := fFileBeingCopied;
  finally
    fCS.Release;
  end;
end;

Also you only set the fFileBeingCopied field, but never reset it, so it will always equal the last copied file, even when the thread is blocked. You should set that string empty when the last file has been copied, and of course do that while the critical section is acquired. Simply move the assignment past the if block.

过潦 2024-08-24 18:35:51

如果您有点不愿意像 mghie 解决方案,另一种可能更快的替代方案是使用 Andreas Hausladen 的 AsyncCalls

骨架代码:

procedure MoveFile(AFileName: TFileName; const DestFolder: string);
//------------------------------------------------------------------------------
begin
  if DestFolder > '' then
    if CopyFile(PChar(AFileName), PChar(IncludeTrailingPathDelimiter(DestFolder) + ExtractFileName(AFileName)), False) then
      SysUtils.DeleteFile(AFileName)
    else
      RaiseLastOSError;
end;

procedure DoExport;
//------------------------------------------------------------------------------
var
  TempPath, TempFileName: TFileName;
  I: Integer;
  AsyncCallsList: array of IAsyncCall;
begin
  // find Windows temp directory
  SetLength(TempPath, MAX_PATH);
  SetLength(TempPath, GetTempPath(MAX_PATH, PChar(TempPath)));

  // we suppose you have an array of items (1 per file to be created) with some info
  SetLength(AsyncCallsList, Length(AnItemListArray));
  for I := Low(AnItemListArray) to High(AnItemListArray) do
  begin
    AnItem := AnItemListArray[I];
    LogMessage('.Processing current file for '+ AnItem.NAME);
    TempFileName := TempPath + Format(AFormatString, [AnItem.NAME, ...]);
    CreateYourFile(TempFileName);
    LogMessage('.File generated for '+ AnItem.NAME);
    // Move the file to Dest asynchronously, without waiting
    AsyncCallsList[I] := AsyncCall(@MoveFile, [TempFileName, AnItem.DestFolder])
  end;

  // final rendez-vous synchronization
  AsyncMultiSync(AsyncCallsList);
  LogMessage('Job finished... ');
end;

If you're somewhat reluctant to go down to the metal and deal with TThread directly like in mghie solution, an alternative, maybe quicker, is to use Andreas Hausladen's AsyncCalls.

skeleton code:

procedure MoveFile(AFileName: TFileName; const DestFolder: string);
//------------------------------------------------------------------------------
begin
  if DestFolder > '' then
    if CopyFile(PChar(AFileName), PChar(IncludeTrailingPathDelimiter(DestFolder) + ExtractFileName(AFileName)), False) then
      SysUtils.DeleteFile(AFileName)
    else
      RaiseLastOSError;
end;

procedure DoExport;
//------------------------------------------------------------------------------
var
  TempPath, TempFileName: TFileName;
  I: Integer;
  AsyncCallsList: array of IAsyncCall;
begin
  // find Windows temp directory
  SetLength(TempPath, MAX_PATH);
  SetLength(TempPath, GetTempPath(MAX_PATH, PChar(TempPath)));

  // we suppose you have an array of items (1 per file to be created) with some info
  SetLength(AsyncCallsList, Length(AnItemListArray));
  for I := Low(AnItemListArray) to High(AnItemListArray) do
  begin
    AnItem := AnItemListArray[I];
    LogMessage('.Processing current file for '+ AnItem.NAME);
    TempFileName := TempPath + Format(AFormatString, [AnItem.NAME, ...]);
    CreateYourFile(TempFileName);
    LogMessage('.File generated for '+ AnItem.NAME);
    // Move the file to Dest asynchronously, without waiting
    AsyncCallsList[I] := AsyncCall(@MoveFile, [TempFileName, AnItem.DestFolder])
  end;

  // final rendez-vous synchronization
  AsyncMultiSync(AsyncCallsList);
  LogMessage('Job finished... ');
end;
守望孤独 2024-08-24 18:35:51

delphi上

in 为了使解决方案起作用,您需要一个工作队列的工作队列。可以使用字符串清单。但是在任何情况下,您都需要守护队列,以便只有一个线程可以在任何时刻写入它。即使写作线程暂停。

您的应用程序写入队列。因此,必须有一种保护的写作方法。

您的线程读取并从队列中删除。因此,必须有一个守卫的读/删除方法。

您可以使用关键部分来确保在任何时候只能访问这些队列。

A good start for using thread is Delphi is found at the Delphi about site

In order to make your solution work, you need a job queue for the worker thread. A stringlist can be used. But in any case you need to guard the queue so that only one thread can write to it at any single moment. Even if the writing thread is suspended.

Your application writes to the queue. So there must be a guarded write method.

Your thread reads and removes from the queue. So there must be a guarded read/remove method.

You can use a critical section to make sure only one of these has access to the queue at any single moment.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文