您应该使用线程,这是 Delphi 的示例TThread
:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
begin
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(procedure
begin
repeat
// Do some long running stuff (in chunks, so we can update the position)
FPosition := CalculatePosition;
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until SomeCondition;
end
);
Thread.Start;
end;
因此,将此模式应用于您的代码后,我们得到:
type
TMyForm = class(TForm)
private
FPosition: Integer;
procedure ProgressUpdate;
procedure Execute;
end;
procedure TMyForm.ProgressUpdate;
begin
ProgressBar1.Position := FPosition;
end;
procedure TMyForm.Execute;
var
Blob: TBlob;
Thread: TThread;
begin
UniQuery1.SQL.Text := 'SELECT * FROM TABLE1 WHERE FIELD_ID = 1';
UniQuery1.Open;
Blob := UniQuery1.GetBlob('DATA');
FPosition:= 0;
ProgressUpdate;
Thread := TThread.CreateAnonymousThread(
procedure
const
BlockSize = $F000;
var
Buffer: array of Byte;
P: Pointer;
Pos, Count: Integer;
begin
SetLength(Buffer, Blob.Size);
repeat
Count := Blob.Read(Pos, BlockSize, P);
FPosition := Round(Pos / Blob.Size * 100);
Pos := Pos + Count;
P := Pointer(Integer(P) + Count);
// Important: Synchronize will run ProgressUpdate in the main thread!
TThread.Synchronize(nil, ProgressUpdate);
until Count < BlockSize;
end
);
Thread.Start;
end;
我删除了Application.ProcessMessage
并将所有处理移至线程。
线程正在设置FPosition
私有属性并用于TThread.Synchronize
将 ProgressBar 位置设置为FPosition
在主线程中。
如果您的块大小不够大,这可能仍会阻塞 UI(由于过度同步),因此请选择适当的块大小或添加一些更新延迟。
您必须确保UniQuery1
在匿名线程运行时在主线程中不使用对象的连接,或者将连接和查询也移动到线程。
这也可能有重入问题,但它应该让您了解如何使用线程进行后台处理。
PS:在线程中运行查询可能也是一个好主意,特别是如果它可能需要一些时间。