【问题标题】:TThread Wait for User InputTThread 等待用户输入
【发布时间】:2014-01-26 11:00:42
【问题描述】:

我有一个TThread 实例,我想等待用户输入。 线程加载一些东西,等待用户点击一个按钮,然后继续它的任务。

我正在考虑将全局 bool 设置为 true,但这对于我认为的实例不太适用,并且线程必须在循环中检查 var 状态,这似乎有点不专业。

tthread 类是否有安全的方法来等待用户输入?

【问题讨论】:

  • 我会有两个系统事件,一个是线程应该终止的标志,另一个是它应该继续暂停的进程。我会用WaitForMultipleObjects 函数和INFINITE 超时等待它们发出信号的状态,如果其中一个发出信号,请采取行动。然后很容易通过某种称为例如的方法向它们发出信号。 AbortExecutionContinueExecution.
  • @TLama 类似线程会自行挂起,然后按钮会恢复它?
  • 线程将无限期地阻塞在WaitForMultipleObjects 线上,或者直到您发出这些事件之一的信号。单击该按钮,您只需调用线程的方法ContinueExecution,这将发出它自己的私有系统事件的信号。
  • @TLama 哦。我接到你了。这也是个好主意。谢谢。
  • 我一直在考虑this approach。但它当然与 Windows 平台相关联,TEvent 不应该如此。

标签: windows multithreading delphi vcl


【解决方案1】:

您可以使用 SyncObjs 单元中的 TEvent。

TMyThread = class(TThread)
public
    SignalEvent : TEvent;
    procedure Execute; override;
end;


TMyForm = class(TForm)
    procedure Button1Click(Sender : TObject);
public
    myThread : TMyThread;
end;

线程完成其工作,然后等待按钮单击事件发出信号。通过使用 TEvent,您还可以指定超时。 (或 0 无限期等待)。

procedure TMyForm.Button1Click(Sender : TObject);
begin
    // Tell the thread that the button was clicked.
    myThread.SignalEvent.SetEvent;
end;

procedure TMyThread.Execute;
var
    waitResult : TWaitResult;
begin
    // do stuff

    // Wait for the event to signal that the button was clicked.
    waitResult := SignalEvent.WaitFor(aTimeout);

    if waitResult = wrSignaled then
    begin
        // Reset the event so we can use it again
        SignalEvent.ResetEvent;
        // do some more stuff
    end else 
        // Handle timeout or error.
end;

【讨论】:

  • 我只是在下面提出了一些建议,但我可能会使用布尔属性并在设置器中发出事件信号。这将允许将“TStopGoThread”插入到继承链中,而进一步的后代会覆盖 setter。
  • 另外,它是一个 Windows 事件类的包装器。信号量是一个不同的原语。
  • 你是绝对正确的。短暂的大脑放屁。已更正。您的属性建议是完全有效的,但取决于实际的实施。我提供了一个非常基本的代码示例来演示它的用法。我不希望它被直接复制。
【解决方案2】:

我很久以前就使用过 Delphi,所以很遗憾我无法提供非常具体的解决方案,但我可以为您指明正确的方向。您基本上需要信号事件(德尔福术语中的TEvent)。您可以在此处找到更多信息和示例:http://docwiki.embarcadero.com/RADStudio/XE2/en/Waiting_for_a_Task_to_Be_Completed

所以本质上,事件是一个关于你可以等待和发出信号的对象。所以等待输入的信号应该等待事件,并且在按钮按下的方法上发出事件信号,线程将解冻。

【讨论】:

  • 这将只是一个全局范围 TEvent,正如我对 bool 所想的那样。我想那是我唯一能做的。谢谢
  • 不必是全局的。 TEvent 可以是 TThread 后代的私有字段,您可以在 ctor 重载或 Execute 方法的顶部创建它。然后,您可以为 Stop/Go 提供一个公共布尔属性,并在 setter/getter 中发出事件信号。