以下面的代码为例:
for i := (myStringList.Count - 1) DownTo 0 do begin
dataList := SplitString(myStringList[i], #9);
x := StrToFloat(dataList[0]);
y := StrToFloat(dataList[1]);
z := StrToFloat(dataList[2]);
//Do something with these variables
myOutputRecordArray[i] := {SomeFunctionOf}(x,y,z)
//Free Used List Item
myStringList.Delete(i);
end;
//Free Memory
myStringList.Free;
如何使用OmniThreadLibrary进行并行化?可能吗?还是需要重组?
我在每次迭代时调用myStringList.Delete(i);
,因为StringList
很大,并且在每次迭代使用后释放项目对于最小化内存使用非常重要。
答案 0 :(得分:2)
简单回答:你不会。
更多参与答案:在并行操作中,您要做的最后一件事是修改共享状态,例如此删除调用。由于不能保证每个单独的任务都能完成"顺序" - 实际上很可能他们至少赢了一次,这个概率接近了100%非常快,您添加到总工作量的任务越多 - 尝试做类似的事情就是玩火。
您可以随意销毁这些项目并进行序列化,也可以并行执行,完成更快,并销毁整个列表。但我认为没有任何办法可以双管齐下。
答案 1 :(得分:2)
你可以作弊。将字符串值设置为空字符串将释放大部分内存并且将是线程安全的。在处理结束时,您可以清除列表。
Parallel.ForEach(0, myStringList.Count - 1).Execute(
procedure (const index: integer)
var
dataList: TStringDynArray;
x, y, z: Single;
begin
dataList := SplitString(myStringList[index], #9);
x := StrToFloat(dataList[0]);
y := StrToFloat(dataList[1]);
z := StrToFloat(dataList[2]);
//Do something with these variables
myOutputRecordArray[index] := {SomeFunctionOf}(x,y,z);
//Free Used List Item
myStringList[index] := '';
end);
myStringList.Clear;
这段代码是安全的,因为我们永远不会从多个线程写入共享对象。您需要确保您使用的所有通常都是本地的变量都在线程块中声明。
答案 2 :(得分:1)
我不会试图展示如何做你最初提出的问题,因为这不会导致性能提升。甚至没有假设您在提议的并行实现中处理了许多不同的数据争用。
这里的瓶颈是磁盘I / O.将整个文件读入内存,然后处理内容是导致内存问题的设计选择。解决此问题的正确方法是使用管道。
管道的第1步将磁盘上的文件作为输入。这里的代码读取文件的块,然后将这些块分成行。这些行是此步骤的输出。整个文件一次永远不会在内存中。您必须调整您阅读的块的大小。
步骤2将步骤1产生的字符串作为输入。第2步消耗这些字符串并生成向量。这些向量将添加到矢量列表中。
第2步将比第1步更快,因为I / 0非常昂贵。因此,尝试使用并行算法优化任一步骤都无法获得任何好处。即使在单处理器机器上,这种流水线实现也可能比非流水线更快。