【问题标题】:Why does my code run slower if I call it from a separate thread?如果我从单独的线程调用它,为什么我的代码运行速度会变慢?
【发布时间】:2011-01-27 22:17:50
【问题描述】:

我刚刚使用 Delphi 的 TThread 类向应用程序添加了线程。该线程调用一个函数,该函数比较两个文件并打印它们之间不同的位。在我介绍线程之前,应用程序可以完成这个过程并在大约 1 - 2 秒内将输出打印到一个 300KB 的文件上。引入线程后检查同一文件可能需要 30 - 45 秒并导致 50% 的 CPU 峰值(AMD Phenom II 三核),以前您没有注意到峰值。

线程正在执行的代码是:

procedure TForm1.CompareFiles(fil1, fil2 : ansistring; sg : TStringGrid; option : integer; progb : TProgressBar);
var
forg, fpat : file;
byteorg, bytepat : Byte;
byteorgc,bytepatc : ansistring;
arrby : Array Of ansistring;
arrpos : Array Of ansistring;
i,x : integer;
begin

if CRCAdlerGenFile(fil1,1) <> CRCAdlerGenFile(fil2,1) then //Only Run if files arn't same
begin
sg.Cols[0].Clear;
sg.Cols[1].Clear;
i := 0;
x := 0;

AssignFile(forg,fil1);
FileMode := fmOpenRead;
Reset(forg,1);
AssignFile(fpat,fil2);
FileMode := fmOpenRead;
Reset(fpat,1);

//Set Progress Bar
progb.Min := 0;
progb.Max := FileSize(forg);

while NOT eof(forg) do
begin
BlockRead(forg,byteorg,1);
BlockRead(fpat,bytepat,1);
Progb.Position := Progb.Position + 1;
byteorgc := IntToHex(byteorg,2);
bytepatc := IntToHex(bytepat,2);
if byteorg <> bytepat then
begin
x := x + 1;
SetLength(arrby,x);
SetLength(arrpos,x);
arrpos[i] := IntToStr(FilePos(forg));
arrby[i] := bytepatc;
i := i + 1;
end;
end;

CloseFile(forg);
CloseFile(fpat);


case option of
0 : begin //Base 2
    for I := 0 to (Length(arrpos) - 1) do
    begin
    arrpos[i] := IntToBin(StrToInt(arrpos[i]),8);
    end;
    end;

1 : ; //Base 10

2 :  begin //Base 16
    for I := 0 to (Length(arrpos) - 1) do
      begin
        arrpos[i] := IntToHex(StrToInt(arrpos[i]),1);
      end;
    end;

3 : begin //Append $
    for I := 0 to (Length(arrpos) - 1) do
    begin
    arrpos[i] := '$'+IntToHex(StrToInt(arrpos[i]),1);
    end;
    end;

4 : begin //Append 0x
    for I := 0 to (Length(arrpos) - 1) do
    begin
    arrpos[i] := '0x'+IntToHex(StrToInt(arrpos[i]),1);
    end;
    end;
end;


Sg.RowCount := Length(arrpos);
for I := 0 to (Length(arrpos) - 1) do
begin
  sg.Cells[0,i] := arrpos[i];
  sg.Cells[1,i] := arrby[i];
end;

if sg.RowCount >= 16 then
sg.DefaultColWidth := 222
else
sg.DefaultColWidth := 231;
end;

end;

使用的线程代码几乎取自我之前提出的这个问题,稍作名称更改以及介绍和进度条变量,但是在我注意到处理缓慢后添加了它作为我监控它的一种方式。

Link to previous question for threading code.

更新:

固定代码看起来像这样。我完全删除了函数 CompareFiles 并将代码移动到 Thread.Execute 以便于阅读/使用。

 procedure TCompareFilesThread.Execute;
  var
forg, fpat : file;
byteorg, bytepat : Array[0..1023] of byte;
i,z,o : integer;
fil1,fil2 : TFilename;
begin
 //Form1.CompareFiles(FEdit3Text, FEdit4Text, FGrid, FOp, FProg);

begin
  fil1 := Form1.Edit3.Text;
  fil2 := Form1.Edit4.Text;
if Form1.CRCAdlerGenFile(fil1,1) <> Form1.CRCAdlerGenFile(fil2,1) then //Only Run if files arn't same
begin

i := 0;
x := 1;
o := 0;

AssignFile(forg,fil1);
FileMode := fmOpenRead;
Reset(forg,1);
AssignFile(fpat,fil2);
FileMode := fmOpenRead;
Reset(fpat,1);

//Set Progress Bar

while NOT eof(forg) do
begin
    while Terminated = False do
      begin
        BlockRead(forg,byteorg,1024);
        BlockRead(fpat,bytepat,1024);

        for z := 0 to 1023 do
          begin
            if byteorg[z] <> bytepat[z] then
            begin
            synchronize(sProgBarNext);
            by := bytepat[z];
            off := IntToStr(o);
            synchronize(SyncGrid);
            inc(x);
          end;
        inc(o);
        end;
    end;
end;

CloseFile(forg);
CloseFile(fpat);
end;
end;
Free;
end;

同步网格

procedure TCompareFilesThread.SyncGrid;
begin
  form1.StringGrid2.RowCount := x;

    if x >= 16 then
      form1.StringGrid2.DefaultColWidth := 222
    else
      Form1.StringGrid2.DefaultColWidth := 232;

        case op of
          0 : off := IntToBin(StrToInt(off),8);    //Base 2
          1 : ; //Base 10
          2 : off := IntToHex(StrToInt(off),1);//Base 16
          3 : off := '$'+IntToHex(StrToInt(off),1); //Append $
          4 : off := '0x'+IntToHex(StrToInt(off),1);//Append 0x
        end;

  form1.StringGrid2.Cells[0,(x-1)] := off;
  form1.StringGrid2.Cells[1,(x-1)] := IntToHex(by,2);
end;

同步程序

procedure TCompareFilesThread.SProgBarNext;
begin
Form1.ProgressBar1.Position := Form1.ProgressBar1.Position + 1;
end;

【问题讨论】:

  • 在我对您上一个问题的回答中,我写道:“不过,最后要提防的是,TStringGrid 是一个 VCL 控件。您不能对此进行任何操作您创建的新线程(无论您最终如何创建它)。您对网格控件所做的一切都需要从主线程完成。使用TThread.SynchronizeTThread.Queue 将任何 VCL 操作转移到主线程上。”我不认为你会听那个建议。
  • 您访问文件的方式确实是性能不佳的根源:我建议您不要读取单字节块这两个文件,您可以读取大块( 4K 或更多),然后根据需要比较每个字节,并且,不要将字节转换为字符串来执行比较...只需比较字节!!
  • Flat,我对它的运行感到惊讶的原因是与 VCL 的交互,尤其是与字符串网格的交互。但是正如您之前被告知的那样,请阅读发布的链接并考虑这些链接与您的情况有何关系。除非你能做到,否则我们帮不了你。
  • @TheFlatline:5kB?您的文件很可能以 4kb 块的形式存储在磁盘上。最佳情况下,您应该阅读 4KB 的倍数:如果您不喜欢 4KB,请使用 8KB 或 16KB。
  • @TheFlatline:分块读取文件是一种必需的基本编码,我不喜欢称之为优化。一次读取一个字节的文件需要几乎相同的编码工作。

标签: multithreading delphi


【解决方案1】:

这段代码在不同的线程中运行?嗯,一个明显的问题是它使用 VCL 控件。 VCL 不是线程安全的,尝试从主线程外部更新 VCL 属性必然会导致问题。这需要大量重构。线程例程的重点是执行计算。您不应该传入 TStringGrid,也不应该更新进度条。

查看the Synchronize and Queue methods of the TThread class,了解从工作线程与主线程交互的正确方法。这需要一些工作,但你最终会得到更快、更干净的结果。

【讨论】:

    【解决方案2】:

    Delphi 中的默认线程优先级是 tpLower,这可能是因为它运行得比您预期的要慢。其他人正确地指出,这段代码非常危险。甚至不要考虑从 Delphi 中的辅助线程更新 UI 控件。

    【讨论】:

    • 您能否解释一下在具有空闲 CPU 周期的系统上较低的线程优先级如何导致性能下降超过 90%?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-10-12
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    • 2016-10-23
    • 2012-01-02
    • 2019-01-03
    相关资源
    最近更新 更多