2017-04-21 16 views
10

Muszę napisać DLL w Delphi XE7. Chcę użyć TParallel.For w DLL. Biblioteka DLL jest ładowana do aplikacji C++, w której wszystko działa. Jednak po zakończeniu aplikacji lub po wywołaniu funkcji FreeLibrary aplikacja zawiesza się. Jeśli usunę wszystkie pętle TParallel.For i zastąpię je standardowymi pętlami, aplikacja zakończy działanie normalnie.Możliwy zakleszczenie przy wywołaniu FreeLibrary

TParallel.For pętle są bardzo proste:

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

Jeśli utworzyć aplikację Delphi z dokładnie tego samego kodu, wszystko działa idealnie.

Po przeprowadzeniu wielu badań i debugowaniu wygląda na to, że istnieje zakleszczenie, które uniemożliwia wyjście aplikacji C++ po wywołaniu FreeLibrary, ale nie mogę znaleźć miejsca, w którym problem występuje w trybie TParallel.

Wystarczy, aby podsumować sytuację:

  • TParallel.For pętle produkować wszystko kompletne i poprawne wyniki.
  • Dokładnie ten sam kod TParallel.For w Delphi .exe działa poprawnie.
  • Biblioteka DLL jest ładowana, a funkcje są wywoływane i poprawnie wykonywane z poziomu aplikacji C++.
  • Aplikacja C++ zakończy poprawnie, jeśli nie ma połączenia TParallel.For.
  • Aplikacja C++ zawiesza się, jeśli istnieją TParallel.For.
  • Zgaduję, że istnieje zakleszczenie, które występuje, gdy wywoływana jest FreeLibrary.
  • Jeśli korzystam z biblioteki wątków OTL, wszystko działa tak, jak powinno.

Moje pytania są następujące:

Czy ktoś doświadczył tego zachowania?

Jaka jest dobra strategia debugowania w celu znalezienia impasu w tej sytuacji?

Każda rada jest bardzo doceniana.

UPDATE

OK, więc jeśli chcesz, Minimal, kompletne i weryfikowalne przykład, tutaj idziesz (dziękuję 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. 

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

W odpowiedź na bardzo "pomocne" komentarze, "jest defekt w kodzie" i "debugowanie go samemu", dziękuję, to jest to, co robiłem przez zbyt długi czas. Tak więc, jeśli nie ma tu żadnej pomocy, postaram się uzyskać pozwolenie na przejście na OTL, które działa w danej bibliotece DLL.

UPDATE 2

OTL działa dokładnie tak, jak oczekiwano. Tak, tak, istnieje "defekt w kodzie". Poddaję się. Zalecam całkowite zrezygnowanie z Delphi, a następnie możemy przenieść wszystko do C++ i C#. To musi być znacznie lepsze rozwiązanie krótko (i długoterminowe).

+2

Podejrzewam, że 'TParallel' tworzy puli wątków i wymaga wyraźnej porządki, aby zatrzymać im. Plik wykonywalny Delphi jest świadomy tego zachowania i wykonuje czyszczenie, podczas gdy plik wykonywalny C++ tego nie robi. – VTT

+0

W kodzie jest wada. Tylko ty masz kod. Bez [mcve] możemy tylko zgadywać. Nie każ nam zgadywać. Albo wytworzą [mcve], albo zrobisz trochę debugowania. –

+2

Nie trzeba się poddawać i obwiniać ludzi, którzy próbują pomóc. Wygląda na to, że masz dobrego kandydata na raport QP. Zrób to, a także dołącz do grupy deweloperów G + Delphi i zgłoś to tam. David M jest tam aktywny i może pomóc. –

Odpowiedz

3

Widziałem problem podobny do tego, chociaż używałem Delphi 10.0 Seattle, a Delphi EXE ładowało bibliotekę Delphi DLL.

Zresztą rozwiązanie, które wymyśliłem to:

  1. W swojej Delphi DLL, należy najpierw utworzyć swój własny basen gwintu.

  2. Użyj przeciążonej wersji TParallel. Aby pobrać obiekt puli wątków jako ostatni parametr, należy podać własny obiekt puli wątków.

  3. Przed wyładowaniem biblioteki DLL Delphi należy zwolnić obiekt puli wątków.

To podejście rozwiązało problem. Dokumentacja

TParallel.For:

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

Przykład pseudo-kod:

MyPool: TThreadPool; 
MyPool := TThreadPool.Create; 

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

MyPool.Free; 
Powiązane problemy