我有一个大的Delphi 2007项目,我使用AsyncCall
我已经在一个控制台应用程序中提取并测试了多线程代码,一切都很好。
我的Delphi 2007项目产生了一个COM DLL,我有一些用c# - mstest编写的单元测试,调用DLL。
单元测试经常随机挂起(测试通过),我发现它发生在释放ThreadPool的asyncall单元结束部分。
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;
如果我删除
for I := FThreadCount - 1 downto 0 do
FThreads[I].Free;
那么它工作得很好。我无法在一个简单的例子中重现这一点,也想不出任何原因可以在终止时挂起线程。
我相信这与这里提出的问题非常相似:
http://qc.embarcadero.com/wc/qcmain.aspx?d=29843如果一个线程停止了,它从Classes:ThreadProc和调用threadxitthreadxit使用DLL_THREAD_DETACH调用DLLEntryProc。只有一个进程中的线程可以在DLL中初始化或分离例程一次。
问题是com+ dll的最终化是从DLL_THREAD_DETACH,这样类:等待将等待无限期,因为线程不会终止,因为它无法处理它的DLL_THREAD_DETACH直到结束。
我花了几天时间找出原因,有一个工作是手动执行asyncall单元的结束部分。
这与算法无关,因为即使是非常简单的代码也会在com+ dll中引起问题。
下面是复制的步骤:创建com+ DLL创建一个包含以下代码的方法:确保TestAsync被调用,当线程池(来自AsyncCall)清理线程时,你会注意到死锁。
procedure Test(int i: integer); cdecl;
begin
Exit;
end;
procedure TestAsyn;
var
t: IAsyncCall;
begin
t := AsyncCall(@Test, [0])
t.Sync;
end;
看起来你正在尝试做一些你不允许在dll初始化/终结化中做的事情
你需要从其他地方运行那个结束代码。通常这是在dll导出的辅助函数中完成的。另一种选择是在dll的最后一个com对象被释放时运行终结。
导出的函数DllCanUnloadNow
看起来像是终止线程的候选函数。