首页 > 解决方案 > Delphi中的线程启动速度

问题描述

我不确定这个问题是否只与 Delphi 相关,但这就是我使用的,所以我会提到它。

我被告知启动一个新线程,即使是从一个典型实现的线程池中也需要大约 20 - 40 毫秒。我参考了https://docs.microsoft.com/en-us/windows/desktop/procthread/multitasking上的文章,它基本上说 Windows 中的时间片约为 20 毫秒,所以实际上最小线程执行时间是20 毫秒。

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

当我在我的主机上运行它时,(i7-6700)秒表始终报告 0 毫秒完成。有人告诉我,我只是在我的主机上使用 WaitFor “幸运”,通常我应该期望单个线程有 20 毫秒。显然,这意味着试图将线程执行时间降低到 20 毫秒以下是不可能的。

是否有关于启动线程需要多快的明确解释?

我用于测试的代码如下。

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.

标签: multithreadingdelphi

解决方案


在 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 中,切片时间在上下文切换时间成本和响应性之间得到了很好的平衡。即使有办法将其降低到 1ms - 0.5ns,如果硬件架构允许,较低的切片时间并不总是更好。

编辑:某些技术,例如英特尔超线程,允许在同一时间片内同一内核上的多个线程上执行,请参阅评论。


推荐阅读