В случае, если кто-то хочет знать, решение было довольно простым в реализации. Теперь у нас есть рабочие часы прошедшего времени [0:00], которые увеличиваются каждый раз, когда клиентское приложение ожидает, пока сервер DataSnap обслужит запрос. По сути, это то, что мы сделали. (Особая благодарность тем, кто поделился своими решениями, которые помогли мне направить свои мысли.)
Сгенерированные сервером классы (ProxyMethods) должны создаваться в потоке VCL, но выполняться в отдельном потоке. Для этого мы создали класс-оболочку ProxyMethods и класс потока ProxyMehtods (все они придуманы для этого примера, но тем не менее иллюстрируют поток):
ProxyMethods.pas
...
type
TServerMethodsClient = class(TDSAdminClient)
private
FGetDataCommand: TDBXCommand;
public
...
function GetData(Param1: string; Param2: string): string;
...
end;
ProxyWrapper.pas
...
type
TServerMethodsWrapper = class(TServerMethodsClient)
private
FParam1: string;
FParam2: string;
FResult: string;
public
constructor Create; reintroduce;
procedure GetData(Param1: string; Param2: string);
procedure _Execute;
function GetResult: string;
end;
TServerMethodsThread = class(TThread)
private
FServerMethodsWrapper: TServerMethodsWrapper;
protected
procedure Execute; override;
public
constructor Create(ServerMethodsWrapper: TServerMethodsWrapper);
end;
implementation
constructor TServerMethodsWrapper.Create;
begin
inherited Create(ASQLServerConnection.DBXConnection, True);
end;
procedure TServerMethodsWrapper.GetData(Param1: string; Param2: string);
begin
FParam1 := Param1;
FParam2 := Param2;
end;
procedure TServerMethodsWrapper._Execute;
begin
FResult := inherited GetData(FParam1, FParam2);
end;
function TServerMethodsWrapper.GetResult: string;
begin
Result := FResult;
end;
constructor TServerMethodsThread.Create(ServerMethodsWrapper: TServerMethodsWrapper);
begin
FServerMethodsWrapper := ServerMethodsWrapper;
FreeOnTerminate := False;
inherited Create(False);
end;
procedure TServerMethodsThread.Execute;
begin
FServerMethodsWrapper._Execute;
end;
Вы можете видеть, что мы разделили выполнение ProxyMethod на два этапа. Первым шагом является сохранение значений параметров в закрытых переменных. Это позволяет методу _Execute()
иметь все, что ему нужно знать, когда он выполняет фактический метод ProxyMethods, результат которого сохраняется в FResult
для последующего извлечения.
Если класс ProxyMethods имеет несколько функций, вы можете легко обернуть каждый метод и установить внутреннюю переменную (например, FProcID
) при вызове метода для установки частных переменных. Таким образом, метод _Execute()
может использовать FProcID
, чтобы узнать, какой ProxyMethod выполнять...
Вы можете удивиться, почему Нить не освобождается. Причина в том, что мне не удалось устранить ошибку "Ошибка потока: дескриптор недействителен (6)", когда поток выполнял собственную очистку.
Код, вызывающий класс-оболочку, выглядит так:
var
smw: TServerMethodsWrapper;
val: string;
begin
...
smw := TServerMethodsWrapper.Create;
try
smw.GetData('value1', 'value2');
// start timer here
with TServerMethodsThread.Create(smw) do
begin
WaitFor;
Free;
end;
// stop / reset timer here
val := smw.GetResult;
finally
FreeAndNil(smw);
end;
...
end;
WaitFor
приостанавливает выполнение кода до завершения потока ProxyMethods. Это необходимо, потому что smw.GetResult
не вернет нужное значение, пока поток не завершит выполнение. Ключом к увеличению прошедшего времени [0:00], когда поток выполнения прокси-сервера занят, является использование TJvThreadTimer
для обновления пользовательского интерфейса. TTimer
не работает, даже если ProxyMethod выполняется в отдельном потоке, потому что поток VCL ожидает WaitFor
, поэтому TTimer.OnTimer()
не выполняется до тех пор, пока не будет выполнен WaitFor
.
Информационно код TJvTheadTimer.OnTimer()
выглядит так, который обновляет строку состояния приложения:
var
sec: Integer;
begin
sec := DateUtils.SecondsBetween(Now, FBusyStart);
StatusBar1.Panels[0].Text := Format('%d:%.2d', [sec div 60, sec mod 60]);
StatusBar1.Repaint;
end;
person
James L.
schedule
02.03.2012