Delphi 中使用循环的多线程

gstyhher  于 2023-05-06  发布在  其他
关注(0)|答案(1)|浏览(378)

我想做多线程的线程自动分配使用循环(例如循环)在 Delphi 中。当我这样做时,我得到一个范围检查错误或线程正在崩溃。下面是我的代码。但是当我手动运行而不循环时,它不会执行错误或崩溃。

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]);
      Tasks[I] := TTask.Create(
        procedure
        begin
          SumFromThreads[I] := SumUptoNumber(InputNumber[I]);
        end);
      Tasks[I].Start;
      //sleep(1);

    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.
esbemjvw

esbemjvw1#

问题是变量捕获。
在传递给TTask.Create的匿名方法中捕获循环变量I。变量捕获总是通过引用而不是通过值进行。这会导致最后一个任务执行回调时,I的值与循环结束后的值相同--通常比执行循环体时的最后一个值大1。它还导致其他任务可能使用与您期望的值不同的值运行,因为它们与最后一个任务发生的情况相同-您只是没有得到范围检查冲突,而只是访问了错误的数组元素。
Sleep基本上延迟了循环,这导致任务可能在循环变量尚未递增的情况下运行。这就是为什么你不会看到问题,但它并没有解决问题本身。
当在循环中使用匿名方法时,你需要使用循环的索引或基本上改变每个循环迭代的任何东西,你总是需要将匿名方法的创建 Package 在函数本身中,以导致捕获具有不同值的匿名方法的多个示例。
就像这样:

function CreateNewTask(I: Integer): ITask;
begin
  Result := TTask.Create(
    procedure
    begin
      SumFromThreads[I] := SumUptoNumber(InputNumber[I]);
    end);
end;

相关问题