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

Delphi中循环自动分配线程出现越界或崩溃问题求助

Delphi循环创建多线程引发范围错误/崩溃的解决方法

问题根源

你代码里的核心问题是匿名任务捕获了循环变量I。TTask是异步执行的,当任务真正开始运行时,外层的for循环可能已经完成了多次迭代,甚至已经结束,此时循环变量I的值已经不是创建任务时的那个索引了。比如当线程启动时,I可能已经变成了NumberOfThreads,这时候访问SumFromThreads[I]就会触发数组范围检查错误,或者访问到错误的数组元素,导致逻辑异常甚至崩溃。而手动创建线程时,每个线程使用的是独立的变量,不会出现这种捕获循环变量的问题。

修复方案

在循环内部创建一个局部变量,保存当前循环的I值,让匿名函数捕获这个局部变量而非循环变量I。这样每个任务都会绑定到创建它时的索引值,不会被后续的循环迭代影响。

修改后的完整代码

uses
  System.SysUtils, System.Diagnostics, System.Threading, System.Math,
  System.Classes;

var
  NumberOfThreads: Uint8;
  Tasks: array of ITask;
  SumFromThreads, InputNumber: array of cardinal;
  SW: TStopWatch;

function SumUptoNumber(NumberUptoSum: cardinal): cardinal;
begin
  Result := 0;
  for var I := 1 to NumberUptoSum do
    Result := Result + I;
end;

begin
  try
    writeln('How many threads do you want?');
    readln(NumberOfThreads);
    SW := TStopWatch.StartNew;

    SetLength(Tasks, NumberOfThreads);
    SetLength(SumFromThreads, NumberOfThreads);
    SetLength(InputNumber, NumberOfThreads);

    for var I := 0 to NumberOfThreads - 1 do
    begin
      writeln('Enter the value for ' + (I + 1).ToString);
      readln(InputNumber[I]);
      // 新增局部变量保存当前索引,避免捕获循环变量I的问题
      var LocalI := I;
      Tasks[I] := TTask.Create(
        procedure
        begin
          // 使用LocalI而非I访问数组
          SumFromThreads[LocalI] := SumUptoNumber(InputNumber[LocalI]);
        end);
      Tasks[I].Start;
    end;

    TTask.WaitForAll(Tasks);
    SW.Stop;

    for var J := 0 to NumberOfThreads - 1 do
      writeln('The sum upto ' + InputNumber[J].ToString +
        ' calculated using Thread Number ' + J.ToString + ' is ' +
        SumFromThreads[J].ToString);

    SetLength(Tasks, 0);
    SetLength(SumFromThreads, 0);
    SetLength(InputNumber, 0);

    writeln('Duration of this process is ' + ((SW.ElapsedMilliseconds) / 1000)
      .ToString + ' seconds ');
    readln;

  except
    on E: Exception do
    begin
      writeln(E.ClassName, ': ', E.Message);
      readln;
    end;
  end;
end.

关键修改点

  • 在循环内部添加var LocalI := I;,将当前循环的索引值存入局部变量
  • 匿名过程中使用LocalI代替I访问SumFromThreads和InputNumber数组

这样每个任务都会绑定到创建时的正确索引,不会因为循环变量的后续变化而引发数组越界或错误访问的问题。

内容的提问来源于stack exchange,提问作者Ganesamanian Kolappan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:17:32