问题
I have a big Delphi 2007 project, and I use AsyncCall
I have extracted and tested the multithreading code in a single console application and everything works fine.
My Delphi 2007 project produces a COM DLL and I have some unit tests that are written in C# - mstest that calls the DLL.
The unit tests hang quite often randomly (tests passed) and i found out it happens at the AsyncCall unit - finalizatoin section that frees the ThreadPool.
destructor TThreadPool.Destroy;
var
I: Integer;
Call: TInternalAsyncCall;
begin
FMaxThreads := FThreadCount; // Do not allocation new threads
FDestroying := True; // => Sync in this thread because there is no other thread (required for FAsnycCallHead.Free)
// Allow the threads to terminate if there is no task
for I := FThreadCount - 1 downto 0 do
FThreads[I].Terminate;
// Wake up all sleeping threads and keep them awake so they can terminate
SetEvent(FThreadTerminateEvent);
// Wait and destroy the threads
****Hangs here --------> for I := FThreadCount - 1 downto 0 do
****-------> FThreads[I].Free;
ReleaseAutoDeleteAsyncCalls;
// Clean up not yet released AutoDelete InternalAsyncCalls.
while FAsyncCallHead <> nil do
begin
Call := FAsyncCallHead.FNext;
CheckAutoDelete(FAsyncCallHead);
FAsyncCallHead := Call;
end;
CloseHandle(FThreadTerminateEvent);
CloseHandle(FWakeUpEvent);
CloseHandle(FMainThreadSyncEvent);
DeallocateHWnd(FMainThreadVclHandle);
DeleteCriticalSection(FAsyncCallsCritSect);
inherited Destroy;
end;
If I remove
for I := FThreadCount - 1 downto 0 do
FThreads[I].Free;
It then works fine. I can't reproduce this in a simple example and can't think of any reasons that could hang the threads when terminating.
I believe it is very similar to the issues raised here:
http://qc.embarcadero.com/wc/qcmain.aspx?d=29843
If a thread stops, it calls ThreadExit from Classes:ThreadProc And ThreadExit calls the DLLEntryProc with DLL_THREAD_DETACH. Only one thread in a process can be in a DLL initialization or detach routine at a time.
The problem is that the finalization of a com+ dll is run from the DLL_THREAD_DETACH, so that the Classes:TThread.WaitFor will wait indefinitly because the thread won't terminate because it can't handle its DLL_THREAD_DETACH till the finalization finishes.
update
I have spent several days finding out the causes, and there is a work around which is to execute the finalization section of AsyncCall unit manually.
It is nothing to do with the algorithms, because even very simple code can cause problem in com+ dll.
Here are the steps to reproduce:
create a com+ dll create a method that has the following code: make sure TestAsync is called and you will notice the deadlock when the thread pool (from AsyncCall) is cleaning up the threads.
procedure Test(int i: integer); cdecl;
begin
Exit;
end;
procedure TestAsyn;
var
t: IAsyncCall;
begin
t := AsyncCall(@Test, [0])
t.Sync;
end;
回答1:
It looks like you're trying to do something that you're not allowed to do within initialization/finalization of a dll.
You'll need to run that finalization code from somewhere else. Usually this is done inside of a helper function that the dll exports. The other option is to run the finalization when the last com object of your dll is freed.
The exported function DllCanUnloadNow looks like a candidate for terminating the threads.
来源:https://stackoverflow.com/questions/33137336/delphi-asynccalls-deadlock-when-terminating-the-threads-com-dll-finalization