代码之家  ›  专栏  ›  技术社区  ›  Bharat

优雅地终止所有线程

  •  0
  • Bharat  · 技术社区  · 6 年前

    this 在我的一个解决方案中

    为此,我创建了一个 ObjectList

    var
      List: TObjectList<TMyConsumerItem>;
    begin
      { Create a new List. }
      List := TObjectList<TMyConsumerItem>.Create();
    

    后来我做了这样的修改:

    procedure TForm1.DoSomeJob(myListItems: TStringList);
    ...
    for i := 1 to cThreadCount do
        List.Add(TMyConsumerItem.Create(aQueue, aCounter));
    

    点击停止按钮,我正在做这个

    for i := 0 to List.Count - 1 do
      begin
        List.Item[i].Terminate;
      end;
      aCounter.Free;
      aQueue.Free;
    

    在这样做的时候,我的申请被绞死了。这是正确的方法还是我遗漏了什么?

    我正在使用10.2东京

    编辑1:

    unit Unit1;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
    
    type
    
      TMyConsumerItem = class(TThread)
      private
        FQueue : TThreadedQueue<TProc>;
        FSignal : TCountDownEvent; 
      protected
        procedure Execute; override;
      public
        constructor Create( aQueue : TThreadedQueue<TProc>; aSignal : TCountdownEvent);
      end;
    
    
      TForm1 = class(TForm)
        Button1: TButton;
        Memo1: TMemo;
        procedure Button1Click(Sender: TObject);
        procedure StopClick(Sender: TObject);
      private
        { Private declarations }
        List: TObjectList<TMyConsumerItem>;
        aQueue: TThreadedQueue<TProc>;
        aCounter: TCountDownEvent;
        procedure DoSomeJob( myListItems : TStringList);
      public
        { Public declarations }
      end;
    
    var
      Form1: TForm1;
    
    implementation
    
    {$R *.dfm}
    
    uses
      SyncObjs, Generics.Collections;
    
    {- Include TMyConsumerItem class here }
    
    procedure TForm1.Button1Click(Sender: TObject);
    var
      aList : TStringList;
      i : Integer;
    begin
      aList := TStringList.Create;
      Screen.Cursor := crHourGlass;
      try
        for i := 1 to 20 do aList.Add(IntToStr(i));
        DoSomeJob(aList);
      finally
        aList.Free;
        Screen.Cursor := crDefault;
      end;
    end;
    
    procedure TForm1.StopClick(Sender: TObject);
    begin
      for i := 0 to List.Count - 1 do
      begin
        List.Item[i].Terminate;
      end;
      List.Free;
      aCounter.WaitFor;
      aCounter.Free;
      aQueue.Free;
    end;
    
    procedure TForm1.DoSomeJob(myListItems: TStringList);
    const
      cThreadCount = 10;
      cMyQueueDepth = 100;
    var
      i: Integer;
    
      function CaptureJob(const aString: string): TProc;
      begin
        Result :=
          procedure
          var
            i,j : Integer;
          begin
            // Do some job with aString
            for i := 0 to 1000000 do
              j := i;
            // Report status to main thread
            TThread.Synchronize(nil,
              procedure
              begin
                Memo1.Lines.Add('Job with:'+aString+' done.');
              end
            );
    
          end;
      end;
    var
      aThread : TThread;
    begin
      List := TObjectList<TMyConsumerItem>.Create();
      List.OwnsObjects := False;
      aQueue := TThreadedQueue<TProc>.Create(cMyQueueDepth);
      aCounter := TCountDownEvent.Create(cThreadCount);
      try
        for i := 1 to cThreadCount do
           List.Add(TMyConsumerItem.Create(aQueue, aCounter));
        for i := 0 to myListItems.Count - 1 do
        begin
          aQueue.PushItem(CaptureJob(myListItems[i]));
        end;
      finally
    
      end;
    end;
    
    
    constructor TMyConsumerItem.Create(aQueue: TThreadedQueue<TProc>; aSignal : TCountDownEvent);
    begin
     Inherited Create(false);
     Self.FreeOnTerminate := true;
     FQueue := aQueue;
     FSignal := aSignal;
    end;
    
    procedure TMyConsumerItem.Execute;
    var
    aProc : TProc;
    begin
     try
     repeat
      FQueue.PopItem(aProc);
      aProc();
     until Terminated;
     finally
      FSignal.Signal;
     end;
    end;
    end.
    
    2 回复  |  直到 6 年前
        1
  •  1
  •   LU RD    6 年前

    您遗漏了一些关于作业队列如何工作以及如何与线程池交互的重要内容。

    1. 引用自终止的线程是错误的。移除 List ,因为它是无用的。
    2. 为了稍后完成队列,请 aQueue 全球的
    3. 要完成线程池,请将尽可能多的空任务添加到队列中。
    4. 请参阅下面的示例,了解如何实现stop方法。注意,两者 aCounter 阿奎厄 范围必须是全局的。免责声明未经测试,目前未在编译器面前。
    5. 如果需要中止作业任务中正在进行的工作,则必须为每个作业任务提供对全局(范围内)标志的引用,并发出结束任务的信号。
    6. 还有其他库可以执行类似的工作,请参阅 Delphi PPL 或者是久经考验的 OTL library .

    procedure TForm1.StopClick(Sender: TObject);
    var
      i : Integer;
      aThread : TThread;
    begin
      // Kill the worker threads by pushing nil
      for i := 1 to cThreadCount do
        aQueue.PushItem(nil);
    
      // Since the worker threads synchronizes with the main thread,
      // we must wait for them in another thread.
      aThread := TThread.CreateAnonymousThread(
        procedure
        begin
          aCounter.WaitFor; // Wait for threads to finish
          aCounter.Free;
          aQueue.Free;
        end
      );
      aThread.FreeOnTerminate := false;
      aThread.Start;
      aThread.WaitFor;  // Safe to wait for the anonymous thread
      aThread.Free;
    end;
    
        2
  •  1
  •   Stijn Sanders    6 年前

    Terminate 仅设置 Terminated 属性设置为true。线程的内部循环检查 结束 属性,并从 Execute 方法。之后,使用 WaitFor 在主线程中检查线程是否已全部结束,然后再释放队列或线程池对象。