调用FreeLibrary可能存在死锁

Hug*_*ooo 10 c++ delphi dll

我必须在Delphi XE7中编写一个DLL.我想在DLL中使用TParallel.For.DLL被加载到C++应用程序中,一切正常.但是,当应用程序终止或调用FreeLibrary时,应用程序将挂起.如果我删除所有TParallel.For循环并用标准循环替换它们,应用程序将正常退出.

TParallel.For循环非常简单:

TParallel.For(0, inImage.Height -1, 
  Procedure(ty : integer)
  begin
    SomeProcedure(ty);
  end);
Run Code Online (Sandbox Code Playgroud)

如果我用完全相同的代码创建一个Delphi应用程序,一切都很完美.

在做了大量的研究和调试之后,看起来有一个死锁阻止了C++应用程序在调用FreeLibrary时退出,但是我找不到TParallel中的问题所在.

只是总结一下情况:

  • TParallel.For循环全部完成并产生正确的结果.
  • 完全相同的TParallel.For Delphi .exe中的代码正常工作.
  • 加载DLL并调用函数并从C++应用程序中正确执行.
  • 如果没有TParallel.For循环,C++应用程序将正确退出.
  • 如果有TParallel.For循环,C++应用程序将挂起.
  • 我猜测在调用FreeLibrary时会发生死锁.
  • 如果我使用OTL线程库,一切都可以正常工作.

我的问题是:

有没有其他人经历过这种行为?

在这种情况下找到死锁的好调试策略是什么?

任何意见是极大的赞赏.

UPDATE

好的,所以如果你想要最小,完整和可验证的例子,那么你去(谢谢Stephen Ball):

library ADelphiDLL;

uses
  System.SysUtils, System.Classes, Threading, SyncObjs;

function IsPrime (N: Integer): Boolean;
var
 Test: Integer;
begin
 IsPrime := True;
 for Test := 2 to N - 1 do
   if (N mod Test) = 0 then
   begin
     IsPrime := False;
     break; {jump out of the for loop}
   end;
end;

function Prime(Max : integer) : boolean;
var
  tot : integer;
begin
   tot := 0;
   TParallel.For(1, Max, procedure (I: Integer)
    begin
      if IsPrime (I) then
        TInterlocked.Increment (Tot);
    end);
   return true;
   end;

exports Prime;

begin
   IsMultiThread := True;
end.
Run Code Online (Sandbox Code Playgroud)

在C++中:

#include "stdafx.h"

typedef bool(__stdcall *primesf)(int);

void main()
{
HINSTANCE hGetDLL = LoadLibrary(L"ADelphiDLL.dll");
primesf primes = (primesf)GetProcAddress(hGetProcIDDLL, "Primes");
bool result = primes(100);
FreeLibrary(hGetDLL);// <-- Hangs here forever
}
Run Code Online (Sandbox Code Playgroud)

为了回应非常"有用"的评论,"代码中存在缺陷"和"自己调试",谢谢,这就是我长期以来一直在做的事情.因此,如果此处没有任何帮助,我将尝试获得切换到OTL的权限,OTL在相关DLL中有效.

更新2

OTL完全按预期工作.所以,是的,代码中存在"缺陷".我放弃.我将建议完全放弃Delphi,然后我们可以将所有内容转移到C++和C#.这必须是一个更好的短期(和长期)解决方案.

小智 3

我见过类似的问题,尽管我使用的是 Delphi 10.0 Seattle,并且 Delphi EXE 加载了 Delphi DLL。

无论如何,我想出的解决方案如下:

  1. 在您的 Delphi DLL 中,首先创建您自己的线程池。

  2. 使用 TParallel.For 的重载版本,它将线程池对象作为其最后一个参数,并提供您自己的线程池对象。

  3. 在卸载 Delphi DLL 之前,请确保释放线程池对象。

这种方法为我解决了这个问题。

TParallel.有关文档:

http://docwiki.embarcadero.com/Libraries/Berlin/en/System.Threading.TParallel.For

伪代码示例:

MyPool: TThreadPool;
MyPool := TThreadPool.Create;

TParallel.For(1, Max, procedure (I: Integer)
    begin
      if IsPrime (I) then
        TInterlocked.Increment (Tot);
    end, 
MyPool  
);

MyPool.Free;
Run Code Online (Sandbox Code Playgroud)