Mar*_*cel 6 delphi multithreading
我对RestRequest工具比较陌生.我希望有人解决了等待异步调用完成的问题......
我有一个程序,它生成数百个不同的api调用,然后调用处理结果,并将结果传递回调用api执行的过程,然后继续...
唯一的问题是总是做这样的电话,不必穿线,是软件挂起,直到通话结束...要尝试和解决这个问题,我改变RESTRequest.Execute;到RESTRequest.ExecuteAsync();,但现在我的问题是,我的代码继续,而无需等待restrequest的响应.
再次试图绕过这个问题我尝试了几个解决方案,甚至api.RESTRequest.ExecuteAsync().WaitFor;(抛出错误thread error: the handler is invalid (6))
有没有什么方法我可以改变下面的函数作为一个单独的线程运行(只有执行部分在线程中运行很重要)...基本上我只想在每次调用函数时显示一个动画加载图标,并且对于我的其余代码来说,直到此功能完成...
我希望有一个更简单的解决方案,而不是开始使用多线程完全..
码
function run_api_command():boolean;
begin
result := false;
RESTResponse.Content.Empty;
RESTAdapter.Dataset:= ds_action;
RESTAdapter.RootElement:= '';
try
RESTRequest.ExecuteAsync;
if(ds_action.Active = false) then ds_action.Active:=true;
if(ds_action.FieldByName('result').AsString<>'Success') then
begin
showmessage(ds_action.FieldByName('result').AsString);
end
else
begin
RESTAdapter.RootElement:= 'data';
result := true;
end;
except
on E: Exception do
begin
if(e.Message = 'REST request failed: Error sending data: (12007) The server name or address could not be resolved') then
begin
if(messagedlg('Could not connect to server. Would you like to retry?', mterror,[mbYes,mbNo],0)=mrYes) then
begin
result := run_api_command();
end;
end
else
begin
showmessage(RESTResponse.Content);
end;
end;
end;
end;
Run Code Online (Sandbox Code Playgroud)
ExecuteAsync()在工作线程(TRESTExecutionThread它返回的对象)中运行.但是,ExecuteAsync()有一个AFreeThread默认参数True.正如文件明确指出:
当
AFreeThread参数设置为False时,此方法返回对此执行线程的引用.注意:如果
AFreeThread参数设置为True,则该方法返回无效引用.
因此WaitFor(),默认情况下,调用返回的对象指针将崩溃.
即使返回的对象指针有效AFreeThread=True,调用WaitFor()仍然会崩溃.当TThread对象的FreeOnTerminate属性设置True为时,该对象在线程终止时释放其底层API句柄,这会导致WaitFor()"句柄无效"错误失败. TThread.WaitFor()有一个逻辑错误1,无法处理时的情况TThread.FreeOnTerminate=True.
(1这个bug一直在Delphi 6中引入,当时TThread为Kylix支持重写了,在以后的版本中它从未得到纠正.)
如果您希望调用者等待来自REST服务器的响应,请执行以下操作之一:
使用Execute()代替ExecuteAsync(),或至少设置,AFreeThread=False以便您可以在线程对象上调用WaitFor()(或等效MsgWaitForMultipleObjects()),然后处理在主UI线程中执行等待的后果:
使用Execute(),但将整个逻辑移动到您自己的工作线程,并根据需要与主UI线程同步.
更好的解决方案是根本不等待,这意味着重新设计代码流.继续使用ExecuteAsync(),但传递一个完成回调,该回调将在请求完成时调用,并让该回调驱动代码中的下一步.不要主动等待ExecuteAsync完成,让它通知你.
procedure run_api_command();
begin
...
RESTRequest.ExecuteAsync(
procedure
begin
if not RESTResponse.Status.Success then
begin
// do something ...
end else begin
// do something else ...
end;
end,
True
);
end;
Run Code Online (Sandbox Code Playgroud)
延伸到雷米的答案......
"但现在我的问题是我的代码继续而不等待restrequest的响应"
这是异步请求的确切目的.你不应该等待,你应该发送请求并转向别的东西.如果你想捕获结果,你也应该提供一个回调程序,但我没有看到你在代码中这样做.
同步和异步调用是完全不同的设计架构,不能像你希望的那样简单地在它们之间切换.您不能只是将Execute更改为ExecuteAsync而不重新设计其余的.Execute本质上等待调用线程内的响应.但是,ExecuteAsync本质上会在新线程中生成请求,以便您的代码可以在后台执行时继续执行.Async请求中没有等待- 它违背了它们的全部目的.
在您的情况下,最理想的选择是将一个请求与另一个请求相关联.含义,仅发送您的第一个请求.收到响应后,从响应回调中发送下一个请求.
"...因为我想将函数创建为一种"全局api调用"函数,以使编码更少......"
那很好.您仍然可以使用一个主要过程来执行所有工作.是什么时候让这些呼叫在这里重要.但是,按钮的OnClick事件从来不是做这种事情的合适场所.按钮应该做的唯一事情(因为它在主UI线程中)是发起请求.如果您希望UI在此之后完全响应,那么该请求应该以某种方式,形状或形式生成新线程.
无论如何,WaitFor只是不会根据您的要求削减它.您想要一个响应式UI.WaitFor将违反此要求,并锁定您的应用程序的主线程.在另一个线程中执行此工作的全部目的是显示"等待"动画,如果主线程忙于等待任何事情,则不会对其进行动画处理.
编辑
我其实想到了另一种可能性.仍然只产生你的第一个请求.但是,不要将一个请求"链接"到另一个响应中,而是在第一个请求的回调中完成所有其余工作.从那时起,它已经在一个线程中,不一定需要为下一个请求产生另一个线程.只需在第一个请求之后执行您已经生成的现有线程中剩余的所需内容.
另一方面,这是现代编程世界中一个非常普遍的问题......