【问题标题】:Restart Delphi TThread that lives the entire app lifetime重新启动贯穿整个应用程序生命周期的 Delphi TThread
【发布时间】:2011-07-16 20:14:59
【问题描述】:

我创建了一个派生自 TThread 的类,因为我希望做一些异步的东西,但是为了避免创建另一个类,我围绕该线程类构建了整个东西。不确定这是否是好的做法,如果我不能让它工作,那么我想我别无选择,只能重新编码..

问题:我在 FormCreate 上创建线程,分配一些属性,然后在 FormDestroy 上释放它。在 Thread 的构造函数中,我设置了FreeOnTerminate = False。当我单击表单上的按钮时,我Start(); 线程。好的,所以它按预期运行,发生错误(预期!),它被传递给我的错误处理事件,它似乎终止。然后我再次单击该按钮,我得到一个Cannot call Start on a running or suspended thread 错误。

如何在不释放线程的情况下完成线程并重新启动它?

【问题讨论】:

  • 如果你想让一个线程在整个应用程序生命周期中都存在,为什么让它终止(或出现),然后尝试重新启动它?你可以使用一段时间(真)开始..结束;环形。如果你想通过点击按钮来控制线程体的运行,请查看 TEvent、TSimpleEvent 或 TSemaphore。
  • @Martin - 我再次启动它以运行 Execute 例程。 :)
  • 把while循环放到TThread.execute中!在循环的顶部,等待 TEvent、TSimpleEvent 或 Tsemaphore 等待对象。当您希望线程执行循环内的任何操作时,请从您的 Button.onClick() 事件中向等待对象发出信号。您只需要 start() 线程一次 - 在构造函数的末尾。
  • .. 还是不行。显然, TThread.start 是在 TThread 构造函数之外调用的,所以我的建议很糟糕 :(( 所以要么调用构造函数,然后 Start() 要么使用创建线程的“旧系统”挂起,(继承的 create( true)),在构造函数的开头并在结尾调用 resume()。TBH,有时 Borland/Embarcadero 似乎试图使线程尽可能尴尬。resume() 没有问题,只要它只在 TThread 构造函数的末尾使用过。

标签: multithreading delphi


【解决方案1】:

这是我实现它的方式:

procedure TAPIRequest.DoRequest;
begin
  FBusy := True;
  Resume;
end;


procedure TAPIRequest.Execute;
begin
  inherited;

  while not Terminated do begin
    HttpError := False;

    try
      Response := HTTP.Post(URL, Params);
      ParseResponse;

    except
      HttpError := True;
    end;

    if Assigned(OnResponse) then
      OnResponse();

    FBusy := False;
    Suspend;
  end;
end;

【讨论】:

    【解决方案2】:

    捕获线程中的错误,在那里处理它,然后让线程继续工作。例如,要处理错误,您可以简单地将方法排队到主线程以报告错误。我希望你不要让异常离开你的线程 Execute 方法。

    【讨论】:

    • 我基本上是在触发一个 OnError 事件,该事件是在主线程中分配的,它工作得非常漂亮。 :)
    • 按照我的建议做这件事不是更容易吗?你已经发现了错误。只需处理它们并继续运行线程。也许我不明白这个问题。
    • 问题是我希望我的线程运行整个生命周期,所以我不必再次设置属性 - 但这样做使我无法重用同一个实例,因为我只能调用 Start一次。
    • 我讨厌这种在聊天中继续讨论的想法。这对其他读者有什么帮助?我想在我的个人资料上设置一个偏好,上面写着,我从不聊天!
    • 解决方案是所谓的“状态机”,线程等待某些工作完成,然后执行,然后休眠直到再次触发。像这样单线程的想法很正常。
    【解决方案3】:

    一旦线程完成/终止,您将无法重新启动它。在这种情况下,如果线程再次像您在 FormCreate 中所做的那样,您应该只创建一个新实例。

    【讨论】:

    • 这有点毁了 Properties 的目的.. 哦,好吧。 :)
    • 可以将属性与 TThread 后代一起使用,但您的 setter/getter 不太可能像设置 Ffields 那样简单,特别是如果线程要在整个生命周期内运行的应用程序。很多时候,为了避免竞争/损坏,需要对属性集进行锁定/排队。
    • “一旦线程完成/终止,您将无法重新启动线程” - 什么 ???????那是 TThread 的糟糕设计。
    【解决方案4】:
    type
      TMyThread = class(TThread)
      protected
        procedure Execute; override;
      end;
    
    var
      Form1: TForm1;
      MyThread: TMyThread;
      Event: TEvent;
    
    procedure TForm1.FormCreate(Sender: TObject);
    begin
      Event := TEvent.Create(nil,true,false, '');
      MyThread := TMyThread.Create(False);
    end;
    
    procedure TMyThread.Execute;
    begin
      while True do
        begin
          Event.WaitFor(Infinite);
    
          // do something
    
          Event.ResetEvent;
        end;
    end;
    
    procedure RestartThread;
    begin
        Event.SetEvent;
    // if you need check thread status, wait or run, use here 
    // if Event.WaitFor(0) = ...(wrTimeout, wrSignaled)
    end;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多