2016-11-30 3 views
0

J'ai créé une application de serveur datasnap pour gérer les données entre une application Windows et des applications mobiles. Une méthode peut prendre un certain temps, et je veux être en mesure de l'arrêter après un certain temps (Timeout)Méthode d'arrêt après un certain temps dans l'application Delphi Datasnap Server

Comment puis-je y parvenir?

+1

Je pense que vous devez définir "stop". Si, par exemple, votre méthode de serveur prend du temps parce qu'elle attend des données d'un serveur de base de données, voulez-vous simplement signaler au client que la méthode a expiré ou voulez-vous annuler la requête db? – MartynA

+0

Eh bien, je veux juste que le serveur arrête de créer ou d'exécuter tout ce que le serveur fait dans cette méthode. Donc, il finira l'appel et le client peut continuer. – Remi

+1

Dans ce cas, faites le travail fastidieux dans un thread secondaire (je veux dire, secondaire par rapport au thread d'exécution de la méthode) et tuez le thread secondaire si cela prend trop de temps. – MartynA

Répondre

2

Le code ci-dessous montre une façon de fournir une méthode de serveur avec un comportement de temporisation.

La tâche qui peut prendre trop de temps est exécutée dans un thread secondaire démarré dans la méthode serveur. Cette méthode utilise un objet TSimpleEvent (voir l'aide en ligne) pour permettre au thread secondaire de renvoyer à l'unité d'exécution de la méthode serveur qu'il a terminée. La valeur (en millisecondes) que vous spécifiez dans l'appel à Event.WaitFor définit le délai d'attente avant l'expiration de l'appel. Si l'appel à WaitFor sur le SimpleEvent expire, vous pouvez prendre n'importe quelle action pour notifier le client du serveur. Si l'appel à WaitFor renvoie wsSignaled, cela signifie que DBThread doit avoir appelé SetEvent sur l'objet Event avant la période spécifiée lors de l'appel WaitFor expiré. Btw, cet exemple a été écrit pour D7, donc pourrait nécessiter une adaptation mineure pour Seattle. En outre, il utilise un descendant TForm comme "serveur", mais devrait fonctionner aussi bien dans une méthode de serveur DataSnap, puisque le principe est le même. Il n'aborde pas la question de savoir exactement comment arrêter la tâche que vous lancez dans le thread secondaire, car si cela est possible et comment le faire si cela dépend de la tâche. À cause de cela, et le fait que vous ne voudriez probablement pas retarder la méthode du serveur en attendant la fin de DBThread, il ne tente pas de libérer le DBThread, bien que dans le monde réel cela devrait bien sûr être fait.

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; 
+0

Eh bien, a répondu à votre q ou non? Y a-t-il quelque chose que vous ne comprenez pas à propos de ma réponse? – MartynA