【问题标题】:Recreating a TThread Inside a TThread dervied class在线程派生类中创建线程
【发布时间】:2012-11-26 09:30:19
【问题描述】:

我创建了一个从 TThread 类派生的新类,在构造函数上我调用“inherited Create(True);”,然后调用“Resume()”,因为我已经覆盖了 Execute() 调用,现在我想回忆一下Execute()(再次运行线程)而不破坏类实例,所以我在新类中有一个名为“myRestart()”的函数,它调用“inherited Create(True);”并让我能够再次调用“Resume()”并且线程再次工作。

我的问题是,这是一种安全的做法吗?如果我有这个类的多个实例,它也会起作用吗?还是有更好的方法?

谢谢

【问题讨论】:

  • 与其描述代码,不如展示它。
  • 还有人想知道为什么要创建一个挂起的线程,然后在构造函数中恢复它。不要一开始就创建它。
  • 这不是一个安全的做法。即使使用单个实例,它也不会像您想要的那样工作,是的,有更好的方法来做到这一点。但是……你到底想做什么?
  • @DavidHeffernan - 在早期版本的 Delphi 中,如果使用 'inherited Create(False)' 实例化,线程很有可能会在调用时立即运行,并且在 ctor 完成创建之前/初始化字段。这导致线程试图等待尚不存在的队列和类似的灾难。 AFAIK,后来的 Delphi 版本总是创建在继承调用时挂起的 OS 线程,并只存储参数,以便在 ctor 返回时 OS 线程可以运行/保持挂起。
  • @Martin 在 Delphi 7(及更早版本)中,线程直到 AfterConstruction 才会恢复

标签: multithreading delphi delphi-7 tthread


【解决方案1】:

不要到处做这样的事情。如果您希望线程类中的过程/函数运行不止一次,请从 Execute 覆盖中的 while() 循环调用它们,并向线程发出信号以在顶部使用合适的同步对象、信号量或事件来运行代码,说:

TmyThread.Execute;
begin
  while true do
  begin
    someEvent.waitFor(INFINITE);
    if terminated then exit;
    doMyProcedure(params);
    doOtherStuff;
  end;
end;

【讨论】:

    【解决方案2】:

    我认为您必须出示您的重启代码? 因为据我所知,如果线程完成它的 Execute 过程,那么它在操作系统中的状态将更改为 DONE 并再次调用 resume 只会启动该线程,因为它只是主线程中的一个函数,而不是一个真正的单独线程。

    顺便说一句,您可以根据需要使用此示例代码

    unit UWorker;
    
    interface
    
    uses Windows, Classes, Contnrs;
    
    type
      TWorkerThread=class;
    
      TWorkerJob=class
        procedure ExecuteJob(Worker: TWorkerThread); virtual; abstract;
      end;
    
      TWorkerThread=class(TThread)
      private
        FFinished: TObjectList;
        FNotFinished: TObjectList;
      protected
        procedure Execute;Override;
      public
        constructor Create(createSuspended: Boolean);override;
        destructor Destroy; override;
      public
        property Finished: TObjectList read FFinished;
        property NotFinished: TObjectList read FNotFinished;
      end;
    
    
    
    implementation
    
    { TWorkerThread }
    
    constructor TWorkerThread.Create(createSuspended: Boolean);
    begin
      inherited;
      FFinished := TObjectList.Create;
      FNotFinished := TObjectList.Create;
    end;
    
    destructor TWorkerThread.Destroy;
    begin
      FFinished.Free;
      FNotFinished.Free;
      inherited;
    end;
    
    procedure TWorkerThread.Execute;
    var
      CurrentJob: TWorkerJob;
    begin
      while not Terminated do
      begin
        if FNotFinished.Count > 0 then
        begin
          CurrentJob := TWorkerJob(FNotFinished.Items[0]);
          FNotFinished.Extract(CurrentJob);
    
          with CurrentJob do
          begin
            ExecuteJob(Self);
          end;
          FFinished.Add(CurrentJob);
        end else
        begin
          // pass the cpu to next thread or process
          Sleep(5);
        end;
      end;
    
    end;
    
    end.
    

    使用此代码只需创建一个工作人员,然后创建一些作业实例并将它们添加到未完成列表中。 Worker 会一一执行所有作业。 要重新启动作业,只需将其从已完成列表中提取并再次添加到未完成。

    请记住,您必须继承您的作业并覆盖 ExecuteJob 过程。

    【讨论】:

    • -1 TObjectList 不是线程安全的!以您在此处假装的方式从不同线程中添加和删除对象,迟早会导致使用您的代码的应用程序失败。您必须协调列表添加/减去或使用线程安全列表!
    • 亲爱的每个程序员都知道应该使用像互斥锁这样的同步对象来保护共享对象,或者使用线程的同步方法,所以我省略了那部分代码。
    • 重新阅读 OP 问题。你真的认为OP知道如何保护它吗?相信我,很多程序员对线程、内存保护或同步一无所知。另一方面,tere 的线程安全队列和列表在那里。恕我直言,没有代码示例比编码不佳的示例更好。
    猜你喜欢
    • 2021-11-09
    • 1970-01-01
    • 1970-01-01
    • 2020-03-15
    • 1970-01-01
    • 1970-01-01
    • 2020-02-11
    • 1970-01-01
    • 2014-09-02
    相关资源
    最近更新 更多