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
相关产品推荐
相关产品推荐

