You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi多线程计时异常:为何多线程执行耗时高于单线程?

多线程并发执行耗时异常分析

问题背景

在戴尔3571(20核/线程i9)设备上测试并发线程的执行速度,发现结果不符合预期,疑似存在锁机制影响。尝试使用Windows CreateThread、ITask等不同线程API测试,结果一致。

测试代码

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
  TMyThread = class(TThread)
  public
    procedure Execute; override;
  end;

  TForm1 = class(TForm)
    Memo1: TMemo;
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure Log(Sender: TMyThread; Log: string);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Log(Sender: TMyThread; Log: string);
begin
  Memo1.Lines.add(Log);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Thr: array[0..19] of TMyThread;
begin
  for var t := 0 to 10 do
  begin
    var Thread := TMyThread.Create(True);
    Thr[t] := Thread;
    Thread.Priority := TPHigher;
  end;
  for var t := 0 to 10 do
    Thr[t].Resume;
end;

{ MyThread }

procedure TMyThread.Execute;
begin
  Sleep(500);
  try
    var ii: nativeint;
    var Start := GetTickCount;
    for var i := 0 to  750000000 do
        inc(ii);
    var Delta := (GetTickCount - Start);
     Synchronize( procedure begin Form1.Log(Self, Format( 'Done Loading : %dms', [Delta]) ); end );
  except
    asm nop; end;
  end;
end;

end.

测试结果

  • 单线程执行时,单次循环耗时:320ms
  • 10线程并发执行时,各线程耗时:

    Done Loading : 344ms
    Done Loading : 375ms
    Done Loading : 391ms
    Done Loading : 422ms
    Done Loading : 438ms
    Done Loading : 469ms
    Done Loading : 469ms
    Done Loading : 469ms
    Done Loading : 516ms
    Done loading : 531ms

疑问

  1. 多线程执行时各线程耗时是否应接近320ms?
  2. 为什么使用不同线程API(TThread、CreateThread、ITask)测试结果一致?

原因分析

1. 线程调度与上下文切换开销

即使是20核/线程的CPU,10个线程同时运行时,Windows调度器无法保证所有线程持续占用核心不被切换。线程切换需要保存/恢复寄存器状态、刷新缓存,这些操作会消耗额外时间,导致每个线程的总执行时长增加。另外,你将线程优先级设为TPHigher,会让调度器更频繁地调度这些线程,但依然无法完全避免上下文切换的开销。

2. 系统调用的隐性竞争

测试中使用的GetTickCount是系统内核函数,调用时需要从用户态切换到内核态。单线程时这类切换次数少,开销不明显;但多线程同时调用系统函数时,内核态的资源竞争会增加每个调用的耗时,间接拉高了循环的总计时。

3. CPU缓存与内存带宽竞争

虽然每个线程的ii是栈上私有变量,不存在共享锁,但多个线程同时执行内存读写操作(栈内存也属于系统内存),会占用更多内存总线带宽。当带宽饱和时,每个线程的内存访问延迟会增加,进而影响循环执行速度。此外,CPU的缓存一致性协议(如MESI)在多核心场景下,即使操作私有数据,也可能存在隐性的缓存同步开销。

4. 不同线程API的本质一致性

TThread、CreateThread、ITask最终都是依赖Windows内核的线程调度机制实现的,本质都是操作系统层面的线程实体。它们面临的调度规则、资源限制完全相同,因此测试结果不会有明显差异。

结论

多线程执行时各线程耗时不会接近单线程的320ms,无锁的独立计算也会受到线程调度、系统调用开销、内存带宽竞争等隐性因素影响,这些都是多线程环境下的正常现象,并非锁机制导致。

内容的提问来源于stack exchange,提问作者Laurent Meyer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 08:40:28