我已经构建了一个datasnap服务器应用程序,用于处理Windows应用程序和移动应用程序之间的数据。
一种方法可能需要一段时间,我希望能够在一段时间后停止它(超时)。
我怎样才能做到这一点?
答案 0 :(得分:2)
下面的代码显示了一种为超时行为提供服务器方法的方法。
可能需要太长时间的任务在辅助线程中执行
从服务器方法开始。此方法使用TSimpleEvent对象(请参阅联机帮助)来启用
辅助线程,用于通知服务器方法已完成的线程。您在Event.WaitFor
调用中指定的值(以毫秒为单位)定义了在呼叫超时之前等待的时间。
如果SimpleEvent上对WaitFor的调用超时,您可以采取任何操作
喜欢通知服务器的客户端。如果对WaitFor的调用返回wsSignaled,则意味着DBThread必须在调用WaitFor过期时指定的时间段之前调用Event对象上的SetEvent。
它没有解决如何完全停止在辅助线程中启动的任何任务的问题,因为这是否可行以及如何执行它取决于具体是什么任务。因此,以及您可能不希望通过等待DBThread完成来延迟服务器方法的事实,它不会尝试释放DBThread,尽管在现实世界中当然应该这样做。
type
TServer = class;
TDBThread = class(TThread)
private
FServer: TServer;
FEvent: TSimpleEvent;
FCancelled : Boolean;
function GetCancelled: Boolean;
procedure SetCancelled(const Value: Boolean);
public
procedure Execute; override;
constructor Create(AServer : TServer);
property Server : TServer read FServer;
property Event : TSimpleEvent read FEvent;
property Cancelled : Boolean read GetCancelled write SetCancelled;
end;
TServer = class(TForm)
// ignore the fact that in this case, TServer is a descendant of TForm
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
protected
CS : TCriticalSection;
Event : TSimpleEvent;
public
procedure DoServerMethod;
end;
[...]
{ TDBThread }
constructor TDBThread.Create(AServer: TServer);
begin
inherited Create(True); // create suspended
FreeOnTerminate := False;
FServer := AServer;
FEvent := FServer.Event;
end;
procedure TDBThread.Execute;
var
StartTime : Cardinal;
begin
Cancelled := False;
// Following is for illustration ONLY, to simulate a process which takes time.
// Do not call Sleep() in a loop in a real thread
StartTime := GetTickCount;
repeat
Sleep(100);
until GetTickCount - StartTime > 5000;
if not Cancelled then begin
{ TODO : Transfer result back to server thread }
Event.SetEvent;
end;
end;
function TDBThread.GetCancelled: Boolean;
begin
FServer.CS.Enter;
try
Result := FCancelled;
finally
FServer.CS.Leave;
end;
end;
procedure TDBThread.SetCancelled(const Value: Boolean);
begin
FServer.CS.Enter;
try
FCancelled := Value;
finally
FServer.CS.Leave;
end;
end;
procedure TServer.DoServerMethod;
var
DBThread : TDBThread;
WaitResult : TWaitResult;
begin
DBThread := TDBThread.Create(Self);
DBThread.Resume;
WaitResult := Event.WaitFor(1000);
case WaitResult of
wrSignaled : begin
// the DBThread completed
ShowMessage('DBThread completed');
end;
wrTimeOut : begin
// the DBThread time out
DBThread.Cancelled := True;
ShowMessage('DBThread timed out');
// Maybe use PostThreadMessage here to tell the DBThread to abort (if possible)
// whatever task it is doing that has taken too long.
end;
end; {case}
{ TODO : Terminate and dispose of the DBThread }
end;
procedure TServer.FormCreate(Sender: TObject);
begin
CS := TCriticalSection.Create;
Event := TSimpleEvent.Create;
end;
procedure TServer.Button1Click(Sender: TObject);
begin
DoServerMethod;
end;