【问题标题】:Delphi Threads - Freezing while waitingDelphi 线程 - 等待时冻结
【发布时间】:2017-06-09 21:55:07
【问题描述】:

我正在编写一个需要等到目录中存在文件的应用程序。我已经尝试了多种方法,唯一有效的解决方案是使用 Sleep/Application.ProcessMessages。

这是我尝试过的:

使用睡眠/Application.ProcessMessages:

Result := False;
for i := 0 to iTimeout do
begin
  if FileExists(fileName) do
  begin
    updateStatus('Conversion Completed');
    Result := True;
    Break;
  end;
  updateStatus(Format('Checking for file: %d Seconds', [i]));
  Application.ProcessMessages;
  Sleep(1000);
end;

此方法有效,但我无法在应用程序等待时关闭它。此外,我宁愿避免使用 Sleep/Application.ProcessMessages 的一些有据可查的问题。

使用 TThread/TEvent:

type
  TMyThread = class(TThread)
  private
    FEventDone: TEvent;
  public
    constructor Create(CreateSuspended: boolean);
    destructor Destroy;
    procedure Execute; override;
    property EventDone: TEvent read FEventDone;
  end;

  TformThreading = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    waitThread: TMyThread;
    { Private declarations }
  public
    { Public declarations }
  end;

var
  formThreading: TformThreading;

implementation

{$R *.dfm}

{ TformThreading }

procedure TformThreading.Button1Click(Sender: TObject);
var
  res: TWaitResult;
begin
  try
    waitThread.Start;
    res := waitThread.EventDone.WaitFor(INFINITE);
    case res of
      wrSignaled: ShowMessage('wrSignaled');
      wrTimeout: ShowMessage('wrTimeout');
      wrAbandoned: ShowMessage('wrAbandoned');
      wrError: ShowMessage('wrError');
      wrIOCompletion: ShowMessage('wrIOCompletion');
    end;
  except
    on E: Exception do
    begin
      ShowMessage(E.Message);
    end;
  end;
end;

procedure TformThreading.FormCreate(Sender: TObject);
begin
  waitThread := TMyThread.Create(true);
end;

procedure TformThreading.FormDestroy(Sender: TObject);
begin
  waitThread.Free;
end;

{ TMyThread }

constructor TMyThread.Create(CreateSuspended: boolean);
begin
  inherited Create(CreateSuspended);
  FEventDone := TEvent.Create;
end;

destructor TMyThread.Destroy;
begin
  FEventDone.Free;
end;

procedure TMyThread.Execute;
begin
  for i := 0 to iTimeout do
  begin
    if FileExists(fileName) do
    begin
      FEventDone.SetEvent;
      Break;
    end;
    Application.ProcessMessages;
    Sleep(1000);
  end;
end;

我似乎无法让它在等待时不冻结我的主线程,但如果我能解决冻结问题,这似乎是正确的方法。

解决问题的最佳方法是什么?

【问题讨论】:

  • 您确定声称“需要等到目录中存在文件。唯一有效的解决方案是使用 Sleep/Application.ProcessMessages” 正确吗?我猜ReadDirectoryChangesW 可能在这里工作。
  • 链接的答案将在有新闻时发送通知。只需在您的程序中设置一个计时器来显示等待的秒数。
  • waitThread.EventDone.WaitFor(INFINITE); - 当您在 UI 线程处理程序中编写它时,这就是死亡。怎么会期望这个 not 冻结你的程序?不要等待您的线程 - 让 it 在完成时通知主线程。 TThread.Queue 是一种方法,或者只使用 OnTerminate 事件。
  • 您使用的线程完全错误。您的主线程正在等待线程,这正是您不想想要做的事情。如果您的应用程序必须等到文件存在,那么线程将无济于事;您的应用仍需等待。

标签: multithreading delphi


【解决方案1】:

我似乎无法让它在等待时不冻结我的主线程。

当您等待一个对象时,该线程会阻塞,直到对象发出信号。因此,当您的主要线程等待事件时,它会被阻止。您观察到的行为完全符合预期。

查看线程的代码,它会循环直到满足条件,发出事件信号,然后终止。换句话说,该事件没有任何目的。您可以删除它,而是等待线程。

现在,如果你这样做了,你的代码会这样做:

  1. 创建一个线程并开始执行。
  2. 等待该线程完成,阻塞主线程。
  3. 线程完成后继续执行。

因此,就目前而言,您的线程一无所获。你也可以在主线程中执行它的代码。你又回到了你开始的地方。

你需要不同的心态。 GUI 程序在其 UI 中是异步的。你需要遵循这种模式。不要在主线程中等待。相反,一定要使用单独的线程,但在完成时让该线程向主线程发出信号。

这里最简单的方法是为线程实现一个OnTerminate 事件处理程序。这将在线程完成其工作时触发。事件处理程序将在主线程中执行。

更一般地,您可以使用Synchronize 或Queue 向主线程发送事件信号,但在您的情况下,处理终止事件可以满足您的需求。

现在到线程的主体。您的方法基于轮询,并带有睡眠。这会起作用,但它不是非常漂亮。 ReadDirectoryChangesW 函数是系统提供的用于接收文件系统更改通知的机制。那将是一种更优雅的方法。

我的建议是首先使用您当前的轮询方法解决阻塞问题。一旦明确重新考虑使用ReadDirectoryChangesW。

【讨论】:

  • 我会考虑使用ReadDirectoryChangesW 函数。 DirectoryWatch 项目似乎是我尝试做的所有事情的一个很好的例子,所以我将使用它作为指南。
【解决方案2】:

我正在编写一个需要等待文件存在的应用程序 一个目录。我已经尝试了多种方法,并且唯一的 有效的解决方案是使用 Sleep/Application.ProcessMessages。

我不这么认为。完成任务的正确方法应该是使用 ReadDirectoryChangesW 函数等待 FILE_ACTION_ADDED 动作发出信号。

您可以找到一个示例实现,例如在Directory Watch 项目中。

【讨论】:

  • 这个项目似乎有我需要的一切,可以用它作为指导。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-30
  • 2019-05-10
  • 1970-01-01
  • 2012-03-21
  • 2023-04-06
  • 1970-01-01
相关资源
最近更新 更多