Delphi中的线程启动速度

时间:2019-01-10 05:00:47

标签: multithreading delphi

我不确定这个问题是否只与Delphi有关,但这是我所使用的,所以我将参考它。

有人告诉我,即使从通常实现的线程池中启动一个新线程也要花费大约20-40ms。我在https://docs.microsoft.com/en-us/windows/desktop/procthread/multitasking看到过一篇文章,该文章基本上说Windows中的时间片约为20毫秒,因此实际上最小线程执行时间为20毫秒。

我已经写了下面的代码,这是非常基本的。在设置有2个处理器,每个处理器1个内核的VMWare工作站VM中,该时序报告大约需要17毫秒才能完成。

当我在主机上运行它时(i7-6700),秒表始终报告0毫秒以完成。有人告诉我我在主机上使用WaitFor感到很“幸运”,通常我应该为单个线程等待20毫秒。显然,这意味着无法将线程执行时间降低到20ms以下。

关于启动线程需要多长时间有确切的解释?

我用于测试的代码如下。

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
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

  TMyThread=class(TThread)
  public
    Sum:integer;
    procedure Execute;override;

  end;

var
  Form1: TForm1;

implementation

uses
  System.Diagnostics;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  sw:TStopWatch;
  thrd: TMyThread;
  theSum:integer;
begin
  sw:=TStopWatch.StartNew;
  thrd:=TMyThread.Create;
  thrd.WaitFor;
  theSum:=thrd.sum;
  thrd.Free;
  sw.Stop;
  memo1.lines.add('sum: '+theSum.ToString);
  memo1.lines.add('elapsed: '+sw.ElapsedMilliseconds.toString);
end;

{ TMyThread }

procedure TMyThread.Execute;
var
  cntr: Integer;

begin
  inherited;
  sum:=0;
  for cntr := 0 to 100 do
    sum:=sum+cntr;
end;

end.

2 个答案:

答案 0 :(得分:0)

在Win10 x64,i5 6500,Delphi Rio上使用以下代码,我能够获得的最快速度为14-16毫秒:

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
  TForm1 = class(TForm)
    Button1: TButton;
    Label1: TLabel;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

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

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  M: TMyThread;
  S, L: Int64;
begin
  QueryPerformanceCounter(S);
  M := TMyThread.Create;
  M.WaitFor;
  QueryPerformanceCounter(L);
  Label1.Caption := IntToStr(L - S);
  M.Free;
end;

{ TMyThread }

procedure TMyThread.Execute;
begin
  inherited;
end;

end.

这完全是关于操作系统时间片的。即使在具有并行执行的多核/ 超线程系统上,理论上线程启动时间接近零,上下文切换为零,并且线程更早终止,您也可以在下一个时间片中达到它。

多个短任务可以在一个线程中的一个时间片中执行。

如果有多个短操作,但线程初始化需要一些时间,线程池对于立即获取初始化线程很有用。

在OS中,切片时间在上下文切换时间成本和响应度之间得到了很好的平衡。即使there are ways to decrease it到1ms-0.5ns,如果硬件体系结构允许,较低的切片时间也不总是更好。

编辑:某些技术(例如Intel Hyper-Threading)允许在同一时间段内在同一内核上的多个线程上执行,请参见注释。

答案 1 :(得分:0)

使用Delphi对短线程应用程序进行计时可能会产生误导,因为大多数人习惯于在IDE中进行此类计时。如果您在IDE中运行,则线程启动会很慢,如本文所述,大约20-40毫秒。这可能是因为在IDE中运行时,会向“消息”发送有关线程启动和停止的文本,这些文本可能与主IDE线程同步。

我本人和其他人在许多计算机上在IDE外面计时似乎表明,在Windows上,线程的创建/销毁需要0.1到0.4毫秒,具体取决于计算机。当然,请在您自己的目标计算机上进行测试以进行确认。只需确保在IDE外部进行测试即可。

然后,线程池在Delphi中发挥作用。第一,它们可以在IDE内进行调试时极大地加快内部时序。另外,根据线程池,我获得了0.01毫秒的任务启动时间来启动任务。因此,对于线程化时仅花费1-2毫秒的短暂时间,缩短任务启动时间的好处就很有意义。

但是,对于大多数人来说,线程运行时间超过20毫秒左右,使用线程池可能带来的好处微不足道。