delphi 为什么从单独的线程调用代码时,代码的运行速度会变慢?

kzmpq1sx  于 2022-11-23  发布在  其他
关注(0)|答案(3)|浏览(233)

我刚刚使用 Delphi 的TThread类为一个应用程序添加了线程。线程调用一个函数,该函数比较两个文件并打印它们之间不同的位。在我引入线程之前,应用程序可以在大约1 - 2秒内完成这个过程并打印输出到一个300 KB的文件上。引入线程检查之后,同一个文件可能需要30 - 45秒,并导致50%的CPU峰值(AMD龙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;
yhuiod9q

yhuiod9q1#

这段代码在不同的线程中运行?一个明显的问题是它使用了VCL控件。VCL不是线程安全的,试图从主线程之外更新VCL属性肯定会导致问题。这需要进行大量的重构。线程例程的目的是执行计算。不应该传入TStringGrid,也不应该更新进度条。
看一下the Synchronize and Queue methods of the TThread class,了解从工作线程与主线程交互的正确方法。这会花费一些工作,但最终会更快、更干净。

dfddblmv

dfddblmv2#

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

ogq8wdun

ogq8wdun3#

请注意,synchronize(SyncGrid)在等待主线程执行SyncGrid时会停止线程,并在完成时继续执行。TThread.Queue()将方法排队,但不会停止线程,但在您的情况下,它不会很好地工作,因为SyncGrid比thread.execute有更多的工作。
由于大多数工作都是更新GUI(网格和进度条),因此在单独的线程中进行处理并不会带来任何好处。
此外,这也不是线程安全的:

`fil1 := Form1.Edit3.Text;`
`fil2 := Form1.Edit4.Text;`

相反,将这两个值作为线程构造函数中的参数传递。

TCompareFilesThread = class(TThread)
  fil1,fil2 : string;
  constructor Create(Afil1,Afil2 : string);
....
AThread := TCompareFilesThread.Create(Edit3.Text,Edit4.Text);

此代码可能也不安全:

if Form1.CRCAdlerGenFile(fil1,1) <> ....

如果CRCAdlerGenFile()不使用Form1中的其他内容,则从线程中使用是安全的,但不能成为Form1方法。

相关问题