在Delphi Datasnap Server Application中经过一段时间后停止方法

时间:2016-11-30 09:17:25

标签: windows delphi service datasnap delphi-10-seattle

我已经构建了一个datasnap服务器应用程序,用于处理Windows应用程序和移动应用程序之间的数据。

一种方法可能需要一段时间,我希望能够在一段时间后停止它(超时)。

我怎样才能做到这一点?

1 个答案:

答案 0 :(得分:2)

下面的代码显示了一种为超时行为提供服务器方法的方法。

可能需要太长时间的任务在辅助线程中执行 从服务器方法开始。此方法使用TSimpleEvent对象(请参阅联机帮助)来启用 辅助线程,用于通知服务器方法已完成的线程。您在Event.WaitFor调用中指定的值(以毫秒为单位)定义了在呼叫超时之前等待的时间。 如果SimpleEvent上对WaitFor的调用超时,您可以采取任何操作 喜欢通知服务器的客户端。如果对WaitFor的调用返回wsSignaled,则意味着DBThread必须在调用WaitFor过期时指定的时间段之前调用Event对象上的SetEvent。

不过,这个例子是为D7编写的,因此可能需要稍作修改 西雅图。它还使用TForm后代作为“服务器”,但在DataSnap服务器方法中应该同样有效,因为原理是相同的。

它没有解决如何完全停止在辅助线程中启动的任何任务的问题,因为这是否可行以及如何执行它取决于具体是什么任务。因此,以及您可能不希望通过等待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;