Http客户端获取请求
我用Delphi创建了一个HTTP服务器。为了测试服务器响应时间,我创建了一个生成随机地址的http客户端应用程序。问题是当我开始向服务器发送请求时,正在处理它们的一部分。这里是我的代码部分:Http客户端获取请求
正在执行此过程开始发送请求:
procedure TPerformanceTestForm.ExecuteURLs;
var
requests: array of TRequestBuilder;
i: Integer;
Stopwatch: TStopwatch;
Elapsed: TTimeSpan;
begin
SetLength(requests, 10);
EnterCriticalSection(criticalSection);
Stopwatch := TStopwatch.StartNew;
for i := 0 to Length(requests) - 1 do
begin
requests[i] := TRequestBuilder.Create;
end;
// remove this lines from source in order to execute all threads
// for i := 0 to Length(requests) - 1 do
// begin
// requests[i].Terminate;
// end;
Elapsed := Stopwatch.Elapsed;
Seconds := Elapsed.TotalSeconds;
LeaveCriticalSection(criticalSection);
end;
procedure TPerformanceTestForm.btnStopQueriesClick(Sender: TObject);
var
i: Integer;
begin
for i := 0 to Length(requests) - 1 do
begin
// requests[i].WaitFor; // the program crashes
requests[i].Free;
end;
end;
这是TRequestBuilder类的一部分:
TRequestBuilder = class(TThread)
private
fHttpClient: TIdHTTP;
public
Constructor Create; reintroduce;
procedure Execute; override;
end;
Constructor TRequestBuilder.Create;
begin
inherited Create(False); // in order not to start another loop and call start for each instance
// FreeOnTerminate := True; // removed this line; see the first answer to know why
Self.fHttpClient := TIdHTTP.Create;
// HttpWorkBegin and HttWork I get from the first answer
Self.fHttpClient.OnWorkBegin := HttpWorkBegin;
Self.fHttpClient.OnWork := HttpWork;
end;
procedure TRequestBuilder.Execute;
var
request, response: string;
begin
repeat
try
request := GenerateHttpRequest;
response := Self.fHttpClient.Get(request);
log.AddJob(request + ' ---> ' + response + ' ---> ' +
FormatDateTime('dd.mm.yyyy hh:mm:ss', Now));
except
on e: Exception do
begin
errlog.Add(FormatDateTime('dd.mm.yyyy hh:mm:ss', Now) + ' ---> ' +
e.Message);
end;
end;
until (Terminated);
end;
// EDIT: change Execute procedure to avoid socket errors (removed the httpClient from class variables):
procedure TRequestBuilder.Execute;
var
request, response: string;
httpClient: TIdHTTP;
begin
repeat
try
httpClient := TIdHTTP.Create;
try
request := GenerateHttpRequest;
response := httpClient.Get(request);
log.AddJob(request + ' ---> ' + response + ' ---> ' +
FormatDateTime('dd.mm.yyyy hh:mm:ss', Now));
finally
httpClient.Free;
end;
except
on e: Exception do
begin
errlog.Add(FormatDateTime('dd.mm.yyyy hh:mm:ss', Now) + ' ---> ' +
e.Message);
end;
end;
until (Terminated);
end;
**编辑:**当我停止http客户端我得到此错误:模块App.exe中地址004083A0访问冲突。阅读地址FFFFFFFC。
**编辑:**我删除了ExecutreURLs中的第二个for循环,现在程序工作正常(有时引发异常)。我现在的问题是:当我没有终止ExecuteURLs过程中的请求时,程序是否泄漏内存?
**编辑:**当我从执行过程中删除repeat- until循环时,程序工作正常(仅在第一次编辑中引发异常)。当我添加repeat-until循环并从btnStopQueries onclick事件中删除时,我得到几个套接字错误
调用TThread.Terminate()
仅仅设置TThread.Terminated
属性,而不执行其他操作。它并不实际终止线程。一个线程负责定期检查Terminated
,然后在需要时从Execute()
退出。您的代码中没有使用Terminated
属性,因此在您的示例中调用Terminate()
是无用的。
您正在设置FreeOnTerminate=True
在你的主题中。所以不,你没有通过电话Terminate()
泄漏线程。在TIdHTTP
完成工作后,他们将自行解脱。
您的访问冲突最有可能是由于一个或多个线程在您有机会致电Terminate()
之前终止并释放自己的内存。根据经验,使用FreeOnTerminate
规则是,如果你需要从访问线程对象外螺纹自己的代码(比如你是通过跟踪线程,并呼吁他们Terminate()
做),那么DO NOT使用FreeOnTerminate=True
在所有! TThread
对象可能会从任何时刻的内存中消失。在这种情况下,您唯一的优点就是如果您使用TThread.OnTerminate
事件在FreeOnTerminate
线程终止时得到通知。该事件在线程释放之前被触发。否则,请保留FreeOnTerminate=False
,并在完成使用后手动释放线程对象。
一个更安全的办法看起来更像这个:
procedure TPerformanceTestForm.ExecuteURLs;
var
requests: array of TRequestBuilder;
i: Integer;
Stopwatch: TStopwatch;
Elapsed: TTimeSpan;
begin
SetLength(requests, 10);
Stopwatch := TStopwatch.StartNew;
for i := 0 to Length(requests) - 1 do
begin
requests[i] := TRequestBuilder.Create;
end;
// optional, maybe after a timeout...
{
for i := 0 to Length(requests) - 1 do
begin
requests[i].Terminate;
end;
}
for i := 0 to Length(requests) - 1 do
begin
requests[i].WaitFor;
requests[i].Free;
end;
Elapsed := Stopwatch.Elapsed;
Seconds := Elapsed.TotalSeconds;
end;
TRequestBuilder = class(TThread)
private
fHttpClient: TIdHTTP;
procedure HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
procedure HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
protected
procedure Execute; override;
public
constructor Create; reintroduce;
destructor Destroy; override;
end;
constructor TRequestBuilder.Create;
begin
inherited Create(False);
fHttpClient := TIdHTTP.Create;
fHttpClient.OnWorkBegin := HttpWorkBegin;
fHttpClient.OnWork := HttpWork;
end;
destructor TRequestBuilder.Destroy;
begin
fHttpClient.Free;
inherited Destroy;
end;
procedure TRequestBuilder.HttpWorkBegin(ASender: TObject; AWorkMode: TWorkMode; AWorkCountMax: Int64);
begin
if Terminated then SysUtils.Abort;
end;
procedure TRequestBuilder.HttpWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
begin
if Terminated then SysUtils.Abort;
end;
procedure TRequestBuilder.Execute;
var
request, response: string;
begin
request := 'http://localhost/?command=validcommand¶m=value';
response := fHttpClient.Get(request);
// log source: http://stackoverflow.com/questions/26099961/asynchronous-append-to-txt-file-in-delphi
log.AddJob(request + ' ---> ' + response);
end;
我提出了一个全局变量的请求,我把第二个循环为我:= 0到长度(请求) - 1做 开始 请求[一世]。等待; 请求[i] .Free; 结束;作为另一个按钮的onclick事件,但程序崩溃。我还对源代码做了一些修改(请参阅已编辑的问题) – 2014-10-02 09:03:35
您可以在第二个执行过程中查看一下:是否必须使用过程为本地http客户端(本地执行过程)设置OnWorkBegin和OnWork属性从你的答案。当我设置它们时,日志文件的内容有所不同(有些查询与它们的响应没有写在客户端日志文件中) – 2014-10-02 09:43:05
在ExecuteURLs()中你仍然有一个本地'requests'变量,不填充'btnStopQueriesClick()'访问的'requests'变量。你需要在每个线程上调用'Terminate()'和'WaitFor()',然后'Free()'它。切勿释放仍在运行的线程。并且您不需要在每次循环迭代中重新创建'TIdHTTP'。在Execute()的顶部创建一次(如果不在构造函数中),然后在循环中重用它。 – 2014-10-02 14:26:53
你得到什么错误?你需要更具体。 – 2014-10-02 14:28:52
@RemyLebeau错误是在OnFormDestroy和我修复它,但我忘了更新问题 – 2014-10-02 14:36:00