【问题标题】:Access Violation caused by Indy SSL component finalization (PPL / TTask)Indy SSL 组件完成导致的访问冲突 (PPL / TTask)
【发布时间】:2019-04-16 01:46:29
【问题描述】:

我使用的是 Delphi 版本(Delphi 10.2u2)

我的目标是使用 Web 服务来处理通过 Indy 框架的多线程 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...) 错误等...

这是一个错误示例:

经过一番搜索,我发现错误是由于在运行子线程之前释放了所需的库。

确实,由于SSL/Winsock(尤其是)库在未完成的HTTP请求期间被释放,请求的连续性会调用一些不可用的函数并导致空指针异常、访问冲突等......

在使用 TTasks 之前,我使用的是常规 TThread,一切正常,因为在应用程序销毁事件中,我一直等到我的所有 TThread 完成他们的任务,然后才让销毁过程走得更远。

据我所知,我可能错了,Delphi 管理这部分,但似乎是在单元完成后才做的。

对我来说最好的解决方案是能够在我的程序的破坏事件中控制我的 TTasks 的状态,比如。等到我所有的 TTask 完成他们的任务。

但是,当我找不到一种方法来做到这一点而不引发其他类型的异常时,例如“操作已取消”

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 用于我最大的项目之一,我无法部署带有此类错误消息的应用程序 ^_^

亲切的问候,

【问题讨论】:

  • "等到我所有的 TTasks 完成他们的任务" - 看看TTask.WaitForAll()TTask 实现了 ITask 接口。创建一个正在运行的ITask 对象数组,然后您可以在该数组上WaitForAll() 退出应用程序

标签: delphi indy idhttp ppl


【解决方案1】:

谢谢你让我上路雷米。这是问题的完整解决方案。

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

【讨论】:

  • 循环应该是while not TTask.WaitForAll()
  • 我同意它节省了几行代码。我的版本是一种可行的替代方案,可让您在每次循环 WaitForAll 函数时处理其他任务(例如:日志记录)
  • 显然在循环体中你可以做你想做的事,包括调用CheckSynchronize。实际上,我不会在评论中向您解释,而是编辑您的答案。等一下。
猜你喜欢
  • 2010-11-05
  • 1970-01-01
  • 1970-01-01
  • 2012-05-22
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 2016-06-15
相关资源
最近更新 更多