Delphi 我需要TThreads吗?如果是这样,我可以暂停、继续并停止它们吗?

Delphi 我需要TThreads吗?如果是这样,我可以暂停、继续并停止它们吗?,delphi,delphi-xe,Delphi,Delphi Xe,我一直在想,是否有更好的方法来编写我的一些过程,特别是那些需要很长时间才能完成的过程 我总是在主GUI线程上运行所有东西,我现在理解并意识到这是不好的,因为它会使应用程序无响应,Application.ProcessMessages在这里没有真正的帮助 这让我觉得我需要使用TThreads进行长时间的操作,例如复制文件。这也让我想知道一些应用程序是如何让你完全控制的,例如允许你暂停、恢复或停止操作 在我正在处理的一个个人项目中,我有大约3个冗长的操作,其中我显示了一个带有TProgressBar

我一直在想,是否有更好的方法来编写我的一些过程,特别是那些需要很长时间才能完成的过程

我总是在主GUI线程上运行所有东西,我现在理解并意识到这是不好的,因为它会使应用程序无响应,
Application.ProcessMessages
在这里没有真正的帮助

这让我觉得我需要使用TThreads进行长时间的操作,例如复制文件。这也让我想知道一些应用程序是如何让你完全控制的,例如允许你暂停、恢复或停止操作

在我正在处理的一个个人项目中,我有大约3个冗长的操作,其中我显示了一个带有TProgressBar的对话框表单。虽然这确实有效,但我觉得可以做得更好。这些进度对话框可能会显示很长时间,您可能希望取消操作,而不是稍后完成作业

正如我所说,目前我正在运行主Gui线程,我是否需要使用TThreads?我不知道如何或从哪里开始实施它们,因为我以前没有与它们合作过。如果我确实需要线程,它们是否提供了我需要的功能,如暂停、恢复、停止操作等


基本上,我正在寻找一种更好的方法来处理和管理冗长的操作。

是的,这绝对是一种需要线程来完成任务的情况

一个如何暂停/恢复线程并取消线程的小示例

进度通过PostMessage调用发送到主线程。 暂停/恢复和取消由
t简单事件
信号完成

编辑:根据@mghie的评论,这里有一个更完整的示例:

编辑2:显示如何传递线程调用繁重工作的过程

编辑3:添加了更多功能和测试单元

unit WorkerThread;

interface

uses Windows, Classes, SyncObjs;

type
  TWorkFunction = function: boolean of object;

  TWorkerThread = Class(TThread)
  private
    FCancelFlag: TSimpleEvent;
    FDoWorkFlag: TSimpleEvent;
    FOwnerFormHandle: HWND;
    FWorkFunc: TWorkFunction; // Function method to call
    FCallbackMsg: integer; // PostMessage id
    FProgress: integer;
    procedure SetPaused(doPause: boolean);
    function GetPaused: boolean;
    procedure Execute; override;
  public
    Constructor Create(WindowHandle: HWND; callbackMsg: integer;
      myWorkFunc: TWorkFunction);
    Destructor Destroy; override;
    function StartNewWork(newWorkFunc: TWorkFunction): boolean;
    property Paused: boolean read GetPaused write SetPaused;
  end;

implementation

constructor TWorkerThread.Create(WindowHandle: HWND; callbackMsg: integer;
  myWorkFunc: TWorkFunction);
begin
  inherited Create(false);
  FOwnerFormHandle := WindowHandle;
  FDoWorkFlag := TSimpleEvent.Create;
  FCancelFlag := TSimpleEvent.Create;
  FWorkFunc := myWorkFunc;
  FCallbackMsg := callbackMsg;
  Self.FreeOnTerminate := false; // Main thread controls for thread destruction
  if Assigned(FWorkFunc) then
    FDoWorkFlag.SetEvent; // Activate work at start
end;

destructor TWorkerThread.Destroy; // Call MyWorkerThread.Free to cancel the thread
begin
  FDoWorkFlag.ResetEvent; // Stop ongoing work
  FCancelFlag.SetEvent; // Set cancel flag
  Waitfor; // Synchronize
  FCancelFlag.Free;
  FDoWorkFlag.Free;
  inherited;
end;

procedure TWorkerThread.SetPaused(doPause: boolean);
begin
  if doPause then
    FDoWorkFlag.ResetEvent
  else
    FDoWorkFlag.SetEvent;
end;

function TWorkerThread.StartNewWork(newWorkFunc: TWorkFunction): boolean;
begin
  Result := Self.Paused; // Must be paused !
  if Result then
  begin
    FWorkFunc := newWorkFunc;
    FProgress := 0; // Reset progress counter
    if Assigned(FWorkFunc) then
      FDoWorkFlag.SetEvent; // Start work
  end;
end;

procedure TWorkerThread.Execute;
{- PostMessage LParam:
  0 : Work in progress, progress counter in WParam
  1 : Work is ready
  2 : Thread is closing
}
var
  readyFlag: boolean;
  waitList: array [0 .. 1] of THandle;
begin
  FProgress := 0;
  waitList[0] := FDoWorkFlag.Handle;
  waitList[1] := FCancelFlag.Handle;
  while not Terminated do
  begin
    if (WaitForMultipleObjects(2, @waitList[0], false, INFINITE) <>
      WAIT_OBJECT_0) then
      break; // Terminate thread when FCancelFlag is signaled
    // Do some work
    readyFlag := FWorkFunc;
    if readyFlag then // work is done, pause thread
      Self.Paused := true;
    Inc(FProgress);
    // Inform main thread about progress
    PostMessage(FOwnerFormHandle, FCallbackMsg, WPARAM(FProgress),
      LPARAM(readyFlag));
  end;
  PostMessage(FOwnerFormHandle, FCallbackMsg, 0, LPARAM(2)); // Closing thread
end;

function TWorkerThread.GetPaused: boolean;
begin
  Result := (FDoWorkFlag.Waitfor(0) <> wrSignaled);
end;

end.
及表格:

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 163
  ClientWidth = 328
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 120
  TextHeight = 16
  object Label1: TLabel
    Left = 79
    Top = 18
    Width = 51
    Height = 16
    Caption = 'Task idle'
  end
  object Label2: TLabel
    Left = 32
    Top = 18
    Width = 41
    Height = 16
    Caption = 'Status:'
  end
  object btnStartTask: TButton
    Left = 32
    Top = 40
    Width = 137
    Height = 25
    Caption = 'Start'
    TabOrder = 0
    OnClick = btnStartTaskClick
  end
  object btnPauseResume: TButton
    Left = 32
    Top = 71
    Width = 137
    Height = 25
    Caption = 'Pause/Resume'
    Enabled = False
    TabOrder = 1
    OnClick = btnPauseResumeClick
  end
  object btnCancelTask: TButton
    Left = 32
    Top = 102
    Width = 137
    Height = 25
    Caption = 'Cancel'
    Enabled = False
    TabOrder = 2
    OnClick = btnCancelTaskClick
  end
end

您还可以使用更高级别的库来执行线程,例如:


多年前,一位名叫Martin Harvey的人写了一篇关于多线程的有用介绍。他的教程可以在上找到-看起来他还上传了一个示例类,该类可以完成您正在寻找的内容,但我还没有看过,所以不能肯定。

如果中的示例代码太复杂,您不喜欢,那么可能更合您的意


使用此功能,您可以将组件拖放到表单中,并为其各种事件添加处理程序(
OnWork
OnWorkProgress
OnWorkFeedback
OnWorkComplete
)。组件将在后台执行
OnWork
事件处理程序,同时从GUI线程执行其他事件处理程序(负责必要的上下文切换和同步)。但是,要在
OnWork
事件处理程序中编写代码,仍然需要彻底了解从辅助线程可以做什么和不能做什么。

您需要向线程发出暂停或取消的信号。线程必须检查该信号,或者可以挂起然后恢复。像全局var这样的信号更好、更有组织。您也可以使用互斥作为信号…@Benjamin Suspend and Resume?不是真的。不应使用这些Windows函数。@BenjaminWeiss不是这样的:暂停正在运行的线程。Suspend原本打算由调试器使用,但在2010年RAD Studio XE中不推荐使用。唯一可靠的方法是让线程定期检查暂停或取消信号。@Benjamin然后阅读SuspendThread的文档。它也这么说。想想看。您认为TThread.Suspend是如何实现的?继续猜测调用了哪个Windows API。这实际上并没有示例代码那么好。暂停的线程仍然会每秒唤醒几十次。您将如何同步关闭线程和销毁外部拥有的事件对象?只是简单地给他们发个信号,等一等,期待最好的结果?您需要对共享对象使用引用计数,或者在释放事件之前释放线程(即,不要使用
FreeOnTerminate
),以正确的方式执行此操作。@JonasWielicki、
Suspend
Resume
从未缩进以供调试器以外的任何其他进程使用。自Delphi XE以来,它们已被弃用。请看。@LU-RD:事实上,我应该将构造函数参数修改为一个
HWND
,而不是一个表单引用;仍然可以传递窗体句柄,但也可以传递帮助程序窗口的句柄。@Blobby,添加了一个如何传递繁重工作过程的示例。@Blobby,这是正确的。您可以根据需要创建任意多个工作过程,只要它们具有相同的调用结构。有关声明各种格式的过程类型的信息,请参见。
object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 163
  ClientWidth = 328
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -13
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 120
  TextHeight = 16
  object Label1: TLabel
    Left = 79
    Top = 18
    Width = 51
    Height = 16
    Caption = 'Task idle'
  end
  object Label2: TLabel
    Left = 32
    Top = 18
    Width = 41
    Height = 16
    Caption = 'Status:'
  end
  object btnStartTask: TButton
    Left = 32
    Top = 40
    Width = 137
    Height = 25
    Caption = 'Start'
    TabOrder = 0
    OnClick = btnStartTaskClick
  end
  object btnPauseResume: TButton
    Left = 32
    Top = 71
    Width = 137
    Height = 25
    Caption = 'Pause/Resume'
    Enabled = False
    TabOrder = 1
    OnClick = btnPauseResumeClick
  end
  object btnCancelTask: TButton
    Left = 32
    Top = 102
    Width = 137
    Height = 25
    Caption = 'Cancel'
    Enabled = False
    TabOrder = 2
    OnClick = btnCancelTaskClick
  end
end