我多年前使用过Delphi,我记得在版本7中,即使那时我也没有太多的知识,从那时起我就用C ++工作,目前我正在使用Delphi XE10在一家公司工作并实现了搜索我发现了许多不同版本的随机内容,这让我有点困惑。
我正在玩线程,我想更新GUI并搜索我发现Synchronize
及其工作方式显示了它对性能的影响,您可以注意到应用程序冻结。
我想知道是否有更顺畅的方法来处理这个问题,比如事件,通知或其他什么?
@edit
unit WriterThreadUnit;
interface
uses
System.Classes, System.SysUtils, Unit1;
type
TWriterThread = class(TThread)
private
linesToPrint: integer;
fileDirectory: string;
function generateFilename(): string;
procedure write();
protected
procedure Execute; override;
public
constructor Create
(
const createSuspended: boolean;
const linesToPrint: integer;
fileDirectory: string
);
end;
implementation
{ TWriterThread }
constructor TWriterThread.Create
(
const createSuspended: boolean;
const linesToPrint: integer;
fileDirectory: string
);
begin
Self.linesToPrint := linesToPrint;
Self.fileDirectory := fileDirectory;
Self.FreeOnTerminate := true;
inherited Create(CreateSuspended);
end;
procedure TWriterThread.Execute;
begin
inherited;
write;
end;
function TWriterThread.generateFilename: string;
begin
Result := Format('%s\%s_total_lines_%d.txt',
[
Self.fileDirectory,
FormatDateTime('hh-mm-ss-zzz', Now),
self.linesToPrint
]
);
end;
procedure TWriterThread.write;
var
fileLines: TStringList;
i: integer;
filename: string;
begin
fileLines := TStringList.Create;
filename := generateFilename;
try
for I := 1 to Self.linesToPrint do
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.Add('Writing: ' + IntToStr(I) + ' to ' + generateFilename);
end
);
fileLines.Add(Format('Line number: %d', [i]));
Sleep(1);
end;
fileLines.SaveToFile(filename);
finally
fileLines.Free;
end;
end;
end.
答案 0 :(得分:2)
您的代码没有错 - 它只是花了很多时间在很多很小的VCL GUI更新中。
我怀疑Memo重绘不是快速程序(当Memo逐行填充数千个字符串时,注意BeginUpdate/EndUpdate
影响。因此尽可能少地进行同步调用(如果需要,发送字符串包)。实际上,用户无法在第二次看到1000次更新。
答案 1 :(得分:2)
为什么不像其他人所说的那样尝试修复当前的代码? 你不应该每秒更新GUI数千次,你是否意识到这没有任何意义?但是,即使您的示例工作也没有冻结我的机器上的GUI(我的机器已经老了)。
以下是如何加快速度的示例代码。
procedure TWriterThread.write;
var
fileLines, memoLines: TStringList;
i: integer;
filename: string;
procedure InternalSynchronize;
begin
Synchronize(
procedure
begin
Form1.Memo1.Lines.BeginUpdate;
Form1.Memo1.Lines.AddStrings(memoLines);
SendMessage(Form1.Memo1.Handle, EM_LINESCROLL, 0, Form1.Memo1.Lines.Count); //used to scroll down the memo
Form1.Memo1.Lines.EndUpdate;
end
);
end;
begin
fileLines := TStringList.Create;
memoLines := TStringList.Create;
filename := generateFilename;
try
for I := 1 to Self.linesToPrint do
begin
if i mod 50 = 0 then //Update GUI one per 50 writes.
InternalSynchronize;
memoLines.Clear;
memoLines.Add('Writing: ' + IntToStr(I) + ' to ' + generateFilename);
fileLines.Add(Format('Line number: %d', [i]));
end;
InternalSynchronize;
fileLines.SaveToFile(filename);
finally
fileLines.Free;
memoLines.Clear;
end;
end;
BTW:如果Memo1
在更新期间闪烁,您可能会考虑在表单上使用DoubleBuffered属性。
答案 2 :(得分:0)
我个人认为你应该像3D游戏那样“限制最大FPS”。
我认为你不会试图向用户展示一些流畅的形状,将一个流畅地转换为另一个,而是希望他看到一些文字信息。
然后你必须给用户时间来查看和阅读文本。 如果你不这样做 - 那就没有意义甚至显示它。
所以我认为你应该
在表单上设置TTimer
,每秒更新几次。根据我对简单文本的经验,如“完成工作的12.5%”或“处理4321的123个单元”,频率为每秒2到4次重新渲染(TTimer
延迟250到500之间毫秒)。你的输出越复杂 - 人类阅读它所需的时间越多 - 频率就越低。
线程应将要渲染的数据发送到特殊的多写入器/单读取器缓冲区中。对于像我上面概述的简单文本,Windows消息已经足够了(只是不要忘记你的线程应该使用PostMessage
从不SendMessage
)。对于更复杂的数据,我建议您使用iOmniBlockingCollection
中的OmniThreads Library
- http://otl.17slon.com
您TTimer
事件应该检查新数据的可用性,尽可能快地呈现 - 使用TStrings.BeginUpdate
TMemo.Lines
真正重要 - 并从缓冲区中删除
当流程结束时,您停止TTimer
并发出无序数据/表单更新。
这样你可以限制FPS以消除闪烁,并且两者都可以让用户看到任何东西并释放CPU无意义的工作。
另见我对
的回答