Возможный тупик при вызове FreeLibrary

Мне нужно написать DLL в Delphi XE7. Я хочу использовать TParallel.For в DLL. DLL загружается в приложение С++, где все работает. Однако, когда приложение завершается или выполняется вызов FreeLibrary, приложение зависает. Если я удалю все петли TParallel.For и заменим их стандартными циклами, приложение нормально завершится.

Циклы TParallel.For очень просты:

TParallel.For(0, inImage.Height -1, 
  Procedure(ty : integer)
  begin
    SomeProcedure(ty);
  end);

Если я создаю приложение Delphi с точно таким же кодом, все работает отлично.

После много исследований и отладки, похоже, что существует тупиковая ситуация, которая препятствует выходу приложения С++ при вызове FreeLibrary, но я не могу найти, где проблема в TParallel.

Просто подведем итог ситуации:

  • TParallel.For полностью завершает и производит правильные результаты.
  • Точно такой же TParallel.For код в Delphi.exe работает правильно.
  • DLL загружается и функции вызываются и выполняются корректно из приложения С++.
  • Приложение С++ выйдет правильно, если нет циклов TParallel.For.
  • Приложение С++ будет висеть, если есть петли TParallel.For.
  • Я предполагаю, что существует тупик, который возникает при вызове FreeLibrary.
  • Если я использую библиотеку потоков OTL, все работает так, как должно.

Мои вопросы:

Кто-нибудь еще испытал такое поведение?

Что такое хорошая стратегия отладки, чтобы найти тупик в этой ситуации?

Любые советы приветствуются.

ОБНОВЛЕНИЕ

Хорошо, поэтому, если вы хотите использовать минимальный, полный и проверенный пример, здесь вы идете (спасибо 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.

в С++:

#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
}

В ответ на очень "полезные" комментарии "есть недостаток в коде" и "отлаживать его самостоятельно", спасибо, это то, что я делал слишком долго. Итак, если здесь не будет никакой помощи, я попытаюсь получить разрешение на переход на OTL, который работает в соответствующей DLL.

ОБНОВЛЕНИЕ 2

OTL работает точно так, как ожидалось. Итак, да, есть "дефект в коде". Я сдаюсь. Я порекомендую вообще отказаться от Delphi, и тогда мы сможем переместить все на С++ и С#. Это должно быть гораздо более короткое (и долгосрочное) решение.

Ответ 1

Я видел проблему, подобную этой, хотя я использовал Delphi 10.0 Seattle и Delphi EXE загружал DLL Delphi.

В любом случае, решение, которое я придумал, следующее:

  • В вашей DLL Delphi сначала создайте свой собственный пул потоков.

  • Используйте перегруженную версию TParallel.For, которая принимает объект пула потоков в качестве последнего параметра и предоставляет свой собственный объект пула потоков.

  • Перед выгрузкой DLL Delphi убедитесь, что вы освободили объект пула потоков.

Этот подход решил проблему для меня.

TParallel.For документация:

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;