Delphi中Task.Run结合TThread.Synchronize模拟回调行为的可行性探究
关于Delphi移动应用中服务器可达性检测与后续逻辑执行的问题
我正在使用Delphi的TRESTRequest.ExecuteAsync查询REST API并解析结果,该方式不会阻塞主线程。由于这是移动应用,设备会频繁进出网络覆盖范围,因此希望在调用API前快速检测服务器是否可达。尝试过TRESTClient的超时机制,但即使开启飞行模式,也需要30秒才会触发超时,效率太低。
我有一个可靠的同步函数CheckServerIsAvailable,为避免阻塞主线程,通过Task.Run调用它,并使用TThread.Synchronize在完成后更新UI,目前该方式运行稳定。但我不想将错误检查、JSON解析等后续逻辑全部写在TThread.Synchronize的匿名过程中,否则代码会很快变得难以阅读和维护。
根据TThread.Synchronize的文档说明,其匿名过程会在主线程执行,因此我尝试在其中调用另一个过程来继续执行后续逻辑,代码如下:
procedure TForm1.Button1Click(Sender: TObject); begin StartWithPart1; end; procedure TForm1.StartWithPart1; begin Label1.Text := 'Looking...'; AniIndicator1.Enabled := true; TTask.Run( procedure begin Connected := CheckServerIsReachable; // 这是阻塞调用 TThread.Synchronize(nil, procedure begin Label1.Text := ifthen(Connected, 'Connected', 'Not connected'); AniIndicator1.Enabled := false; ShowMessage('leaving part 1'); ContinueWithPart2; end); end); end; procedure TForm1.ContinueWithPart2; begin ShowMessage('entering part 2'); // 这里是我想调用REST接口、处理错误和解析结果的地方 end;
这段代码看似正常工作:TAniIndicator动画正常,标签更新正确,但运行时“entering part 2”的消息框会先于“leaving part 1”显示,这让我怀疑自己对TThread.Synchronize的理解存在偏差。我想请教:
- 这种实现方式是否合理?
- 如果
TThread.Synchronize确实在主线程执行,为什么消息框的显示顺序不符合预期? - 有没有更优的实现模式来满足我的需求?
更新
后来了解到移动平台上的对话框是异步的,其显示顺序与创建顺序无关,感谢大家的帮助。
为完整起见,我尝试了另一种基于子类化TThread的方案,该方案同样有效且代码可读性更高,具体实现如下:
ServerCheck单元代码
unit ServerCheck; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Threading, IdTCPClient; type TServerCheck = class(TThread) Connected: Boolean; TCPClient: TIdTCPClient; private function IsServerReachable: Boolean; public constructor Create(host: string; port: Integer; timeout: Integer; callback: TNotifyEvent); destructor Destroy; override; procedure Execute; override; end; implementation const BUSY = 1; NOT_BUSY = 0; constructor TServerCheck.Create(host: string; port: Integer; timeout: Integer; callback: TNotifyEvent); begin inherited Create(true); FreeOnTerminate := true; OnTerminate := callback; TCPClient := TIdTCPClient.Create; TCPClient.host := host; TCPClient.port := port; TCPClient.ReadTimeout := timeout; TCPClient.ConnectTimeout := timeout; end; destructor TServerCheck.Destroy; begin TCPClient.Free; inherited; end; function TServerCheck.IsServerReachable: Boolean; begin try if TCPClient.Tag = NOT_BUSY then begin TCPClient.Tag := BUSY; TCPClient.Connect; TCPClient.Disconnect; TCPClient.Tag := NOT_BUSY; result := true; end else begin result := false; end except begin TCPClient.Tag := NOT_BUSY; result := false; end; end; end; procedure TServerCheck.Execute; begin Connected := IsServerReachable; end; end.
窗体调用代码
procedure TForm1.Button1Click(Sender: TObject); var netCheck: TServerCheck; begin Label1.Text := 'Checking'; AniIndicator1.Visible := true; AniIndicator1.Enabled := true; netCheck := TServerCheck.Create('www.stackoverflow.com', 443, 6000, Callback); netCheck.Start; end; procedure TForm1.Callback(Sender: TObject); begin AniIndicator1.Enabled := false; AniIndicator1.Visible := false; Label1.Text := IfThen(TServerCheck(Sender).Connected, 'Connected', 'Not connected'); end;
内容的提问来源于stack exchange,提问作者Chris Hinch
相关产品推荐
相关产品推荐

