IdThreadComponent и блокировка пользовательского интерфейса в Delphi
При работе с потоками в Delphi, важно учитывать, что потоки выполняются параллельно с основным потоком пользовательского интерфейса (UI). Если не соблюдать осторожность, это может привести к блокировке UI и ухудшению пользовательского опыта. В этой статье мы рассмотрим проблему, когда использование IdThreadComponent блокирует UI после завершения загрузки файла и отправки HTTP POST-запроса.
Проблема
Разработчик использует IdThreadComponent для выполнения простой загрузки файла на FTP-сервер. После завершения загрузки файла отправляется HTTP POST-запрос на сервер, и в зависимости от ответа сервера отображается соответствующее сообщение. После отображения сообщения, приложение блокируется, и пользователь больше не может взаимодействовать с его controls.
Причина проблемы
Проблема заключается в том, что все операции с UI выполняются в основном потоке UI. Когда IdThreadComponent завершает свою работу, событие OnWorkEnd вызывается в том же потоке, что и TIdFTP, а не в основном потоке UI. Attempting to update UI controls or display messages from this non-UI thread results in the UI becoming unresponsive.
Решение проблемы
Чтобы решить эту проблему, необходимо синхронизировать доступ к UI из неглавного потока. Можно использовать TThread.Queue или TIdNotify для этого. Ниже приведены примеры кода, демонстрирующие оба подхода.
Использование TThread.Queue
procedure TfrmNoticeWindow.IdThreadComponent1Run(Sender: TIdCustomThreadComponent);
begin
IdFtp1.Host := 'ip';
IdFtp1.Username := 'user';
IdFtp1.Password := 'pass';
if aborted then Exit;
try
IdFtp1.Connect;
except
TThread.Queue(nil,
procedure
begin
msgDlgBox.MessageDlg('Could not connect!', mtError, [mbOk], 0);
end
);
Exit;
end;
try
if not aborted then
IdFtp1.Put(txtPath.text, file_name);
finally
IdFtp1.Disconnect;
end;
end;
procedure TfrmNoticeWindow.IdThreadComponent1AfterRun(Sender: TIdCustomThreadComponent);
begin
publishing := false;
TThread.Queue(nil,
procedure
begin
uploadGauge.Value := 0;
uploadGauge.Visible := false;
frmNoticeWindow.Height := 512;
btnUpload.Caption := 'Publish';
if not aborted then
begin
txtPath.Text := '';
txtNoticeHeader.Text := '';
end;
end
);
end;
procedure TfrmNoticeWindow.IdFTP1Work(Sender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
if aborted then
IdFtp1.Abort;
end;
procedure TfrmNoticeWindow.IdFTP1WorkEnd(Sender: TObject; AWorkMode: TWorkMode);
var
Params : TStringList;
Resp : String;
begin
if aborted then Exit;
Params := TStringList.Create;
try
Params.Add('enotice_publish='+packet);
Resp := doPost('url', params);
finally
Params.Free;
end;
TThread.Queue(nil,
procedure
begin
if (Resp = 'Notice published successfully!') then
msgDlgBox.MessageDlg(Resp, mtInformation, [mbOk], 0)
else
msgDlgBox.MessageDlg(Resp, mtError, [mbOk], 0);
end
);
end;
Использование TIdNotify
uses
..., IdSync;
procedure TfrmNoticeWindow.MsgBoxCouldNotConnect;
begin
msgDlgBox.MessageDlg('Could not connect!', mtError, [mbOk], 0);
end;
procedure TfrmNoticeWindow.MsgBoxPostOk;
begin
msgDlgBox.MessageDlg('Notice published successfully!', mtInformation, [mbOk], 0)
end;
procedure TfrmNoticeWindow.MsgBoxPostFail;
begin
msgDlgBox.MessageDlg('Notice failed to publish!', mtError, [mbOk], 0);
end;
procedure TfrmNoticeWindow.ResetUiOk;
begin
uploadGauge.Value := 0;
uploadGauge.Visible := false;
frmNoticeWindow.Height := 512;
btnUpload.Caption := 'Publish';
txtPath.Text := '';
txtNoticeHeader.Text := '';
end;
procedure TfrmNoticeWindow.ResetUiAborted;
begin
uploadGauge.Value := 0;
uploadGauge.Visible := false;
frmNoticeWindow.Height := 512;
btnUpload.Caption := 'Publish';
end;
// Остальной код аналогичен первому примеру, но с использованием TIdNotify вместо TThread.Queue
Обе реализации решают проблему блокировки UI, синхронизируя доступ к UI из неглавного потока с помощью TThread.Queue или TIdNotify. Это гарантирует, что все операции с UI выполняются в основном потоке UI, что предотвращает блокировку и обеспечивает плавную работу приложения.
В данном контексте описывается проблема блокировки пользовательского интерфейса в приложении, созданном на Delphi, которая возникает при использовании `IdThreadComponent` для загрузки файла на FTP-сервер и отправки HTTP POST-запроса после завершения загру
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.