我们的应用程序处理测量数据,这些数据必须从测量设备读取并存储在数据库中。
我们提供了一次批量读取和存储多组测量数据的选项。由于这是一个耗时的过程,我们显示一个带有进度条和按钮的模态对话框来取消操作。
只有在读取和存储了一组完整的测量数据之后,才能取消操作。
read & store循环如下:
ItemsToStore := GetSelectedTreeItems();
DlgProgress := TProgressWithAbort.Create(Screen.ActiveForm);
try
for i := 0 to Pred(ItemsToStore.Count) do
begin
if DlgProgress.Cancel then exit;
DlgProgress.Description := ItemsToStore[i].Name;
ReadAndStoreItem(ItemsToStore[i].Id);
DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
end;
finally
DlgProgress.Free;
end;进度对话框位置属性的设置程序调用一个名为CheckMouseButtonInput的过程,该过程目前编码如下:
procedure TProgressWithAbort.CheckMouseButtonInput;
var
Msg: TMsg;
begin
// if the left mouse button was pressed while the mouse was at the
// Cancel button call the application's message loop to process the event
if PeekMessage(Msg, btnCancel.Handle, WM_LBUTTONUP, WM_LBUTTONUP, PM_NOREMOVE) then
Application.ProcessMessages;
end;在应用程序的message循环中,调用以下按钮单击处理程序,它设置可通过属性Cancel访问的变量:
procedure TProgressWithAbort.btnCancelClick(Sender: TObject);
begin
FCancel := true;
end;一切都很好。但是,我不知道上面的CheckMouseButtonInput实现是否占用了太多的CPU时间。在GetQueueStatus或MsgWaitForMultipleObjects之前调用PeekMessage (没有句柄,超时为0)更好吗?
发布于 2017-07-13 21:22:38
由于这是一个耗时的过程,我们显示一个带有进度条和按钮的模态对话框来取消操作。
然后,进程应该移动到一个单独的工作线程。不要在主UI线程中运行冗长的操作。它应该只处理UI,而不是其他任何东西。即使您想阻止主UI线程直到进程完成,您仍然应该让主线程处理消息正常,不要手动执行。
启动线程,显示对话框,如果按下Cancel按钮,则向线程发出终止信号,并在线程退出时关闭该对话框。线程可以在需要时向对话框发送UI更新,并在测量之间检查终止状态。不需要CheckMouseButtonInput()逻辑。
例如:
type
TCancelEvent = procedure of object;
TProgressWithAbort = class(TForm)
btnCancel: TButton;
procedure btnCancelClick(Sender: TObject);
private
FCancel: Boolean;
FOnCancel: TCancelEvent;
public
property Cancel: Boolean read FCancel;
property OnCancel: TCancelEvent read FOnCancel write FOnCancel;
end;
procedure TProgressWithAbort.btnCancelClick(Sender: TObject);
begin
FCancel := true;
if Assigned(FOnCancel) then
FOnCancel();
end;
procedure TMyForm.LengthyProcess;
var
ItemsToStore: TListOfWhatever;
StoreThread: TThread;
DlgProgress: TProgressWithAbort;
begin
ItemsToStore := GetSelectedTreeItems();
DlgProgress := TProgressWithAbort.Create(Self);
try
StoreThread := TThread.CreateAnonymousThread(
procedure
var
i: Integer;
begin
try
for i := 0 to Pred(ItemsToStore.Count) do
begin
if TThread.CheckTerminated then Exit;
TThread.Queue(TThread.CurrentThread,
procedure
begin
DlgProgress.Description := ItemsToStore[i].Name;
end;
);
// make sure this function is thread-safe!
ReadAndStoreItem(ItemsToStore[i].Id);
TThread.Queue(TThread.CurrentThread,
procedure
begin
DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
end
);
end;
finally
DlgProgress.ModalResult := mrClose;
end;
end
);
try
StoreThread.FreeOnTerminate := False;
StoreThread.Start;
try
DlgProgress.OnCancel := StoreThread.Terminate;
DlgProgress.ShowModal;
finally
StoreThread.Terminate;
StoreThread.WaitFor;
end;
finally
StoreThread.Free;
end;
finally
DlgProgress.Free;
end;
end;另一种选择是:
var
ItemsToStore: TListOfWhatever;
StoreThread: TThread;
DlgProgress: TProgressWithAbort;
...
procedure TMyForm.StartLengthyProcess;
begin
ItemsToStore := GetSelectedTreeItems();
StoreThread := TThread.CreateAnonymousThread(
procedure
var
i: Integer;
begin
for i := 0 to Pred(ItemsToStore.Count) do
begin
if TThread.CheckTerminated then Exit;
TThread.Queue(TThread.CurrentThread,
procedure
begin
DlgProgress.Description := ItemsToStore[i].Name;
end;
);
// make sure this function is thread-safe!
ReadAndStoreItem(ItemsToStore[i].Id);
TThread.Queue(TThread.CurrentThread,
procedure
begin
DlgProgress.Position := Succ(i) * 100 div ItemsToStore.Count;
end
);
end;
end
);
StoreThread.OnTerminate := LengthyProcessFinished;
DlgProgress := TProgressWithAbort.Create(Self);
DlgProgress.OnCancel := StoreThread.Terminate;
DlgProgress.Show;
// disable the rest of the UI as needed..
StoreThread.Start;
end;
procedure TMyForm.LengthyProcessFinished(Sender: TObject);
begin
StoreThread := nil;
FreeAndNil(DlgProgress);
// enable the rest of the UI as needed..
end;https://stackoverflow.com/questions/45090903
复制相似问题