因Indy SSL组件完成(PPL / TTask)导致的访问冲突

时间:2018-11-13 09:09:21

标签: delphi indy idhttp ppl

我正在使用Delphi版本(Delphi 10.2u2)

我的目标是通过Indy框架使用Web服务来处理多线程GET,POST命令,没有什么真正复杂的,并且一切正常,直到在请求期间关闭应用程序为止。

为此,我使用TidHTTPClient处理命令,并使用TIdSSLIOHandlerSocketOpenSSL支持流量加密。

对于多线程,我正在使用并行编程库(PPL)中的TTask。 “在Delphi中使用此库是我的新手。”

这是一个代码示例:

procedure TForm3.Button1Click(Sender: TObject);
begin
TTask.Create(
              procedure
              var HTTP : TidHTTP;
                  SSL : TIdSSLIOHandlerSocketOpenSSL;
                  content : String;
              begin
                HTTP := TIdHTTP.Create(nil);

                SSL := TIdSSLIOHandlerSocketOpenSSL.create(nil);

                SSL.SSLOptions.Method := sslvSSLv23;

                HTTP.IOHandler       := SSL;
                HTTP.ConnectTimeout  := 20000;
                HTTP.ReadTimeout     := 60000;
                HTTP.HandleRedirects := true;

                try
                  {
                    If we close the application during the Get, it raise an exception.
                    This is because the SSL Library is freed before the get has the time to stop

                    finalization
                      UnLoadOpenSSLLibrary(); // called before get is over
                  }
                  Content := HTTP.Get('https://www.google.com/');

                  TThread.Synchronize(nil, procedure begin
                     memo1.text := Content;
                  end);
                except
                  // ...
                  on E : Exception do
                    TThread.Synchronize(nil, procedure begin
                      memo1.Text := 'Error';
                    end);
                end;

                SSL.Free;
                HTTP.Free;
  end).Start();
end;

如果编译并执行此代码,您会发现一切正常,直到决定在请求(GET,POST或任何其他方式)期间关闭程序为止

根据HTTP请求期间关闭应用程序的确切时间,错误可能会有所不同。这将是访问冲突,Winsock2(WSA ...)错误等...

这是一个错误示例: Access Violation

经过一番搜索,我发现该错误是由以下事实造成的:所需的库在运行的子线程之前被释放。

实际上,因为未完成的HTTP请求期间释放了SSL / Winsock(尤其是)库,所以请求的连续性将调用某些不可用的函数,并导致空指针异常,访问冲突等...

在使用TTasks之前,我使用的是常规TThread,并且一切都很好,因为在应用程序destroy事件上,我一直等到所有TThread完成其任务后,再进行销毁工作。

据我所知,我可能是错的,Delphi负责这部分工作,但似乎在Units完成后就完成了。

对我来说,最好的解决方案是能够在程序的销毁事件上控制TTasks的状态。等到我所有的TTasks完成任务。

但是,当我无法找到一种方法而又不引发“操作取消”之类的其他异常情况时,

procedure TForm3.FormDestroy(Sender: TObject);
begin
  ATask.Cancel;
  repeat
  if ATask.Wait(500) = false then begin
     CheckSynchronize();
  end;
 until (ATask = nil);
end;

上面的示例不起作用...

我确定我做错了事,在其他地方找不到与该主题有关的任何帮助,Delphi PPL听起来很新,或者我的问题真的很单一。

在此先感谢您的帮助。

我真的想对我最大的项目之一使用PPL,我无法部署带有此类错误消息的应用程序^ _ ^

问候,

1 个答案:

答案 0 :(得分:0)

谢谢您让我走上雷米(Remy)的路。这是问题的完整解决方案。

procedure TForm3.FormDestroy(Sender: TObject);
begin
  while not TTask.WaitForAll(FTasks, 1000) do
    CheckSynchronize();
end;