В случае, если кто-то хочет знать, решение было довольно простым в реализации. Теперь у нас есть рабочие истекшие временные часы [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
, чтобы узнать, какой прокси-метод выполнить ...
Вы можете задаться вопросом, почему нить не освобождает себя. Причина в том, что я не смог устранить ошибку «Ошибка потока: дескриптор недействителен (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
для обновления пользовательского интерфейса. A 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;
Вот что я подумал. Вы бы подумали, что с тем, как долго DataSnap был вокруг, что было бы что-то встроенное для обработки состояния занятости пользовательского интерфейса или что-то еще ... DataSnap также не имеет обратного вызова, чтобы определить пользовательский интерфейс, сколько данных было передано, поэтому пользовательский интерфейс может внедрить индикатор прогресса для больших результатов поиска/толчков. –
(Оставляя это обозначенным как «правильный ответ», потому что решение может принимать любую форму - тот, который приведен ниже, просто возможен. Слишком плохо, вы не можете отметить больше, чем «один» ответ как «правильный».) –