2015-10-14 39 views
-2

時,我有一個很大的德爾福2007年的項目,我用AsyncCall德爾福AsyncCalls死鎖終止的線程(COM + DLL定稿)

我已經提取,並在一個單一的控制檯應用程序測試的多線程代碼,一切工作正常。

我的Delphi 2007項目生成一個COM DLL,我有一些單元測試用C#寫的 - mstest調用DLL。

單元測試經常隨機掛起(測試通過),我發現它發生在AsyncCall單元 - finalizatoin部分釋放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; 

如果我刪除

for I := FThreadCount - 1 downto 0 do 
    FThreads[I].Free; 

然後它工作正常。我不能在一個簡單的例子中重現這一點,也不能想到在終止時可能掛起線程的任何原因。

我相信這是非常相似,在這裏提出的問題:

http://qc.embarcadero.com/wc/qcmain.aspx?d=29843

如果一個線程停止時,它會調用由類ThreadExit:ThreadProc的和 ThreadExit調用與DLL_THREAD_DETACH的DLLEntryProc。一個進程中只有一個 線程可以一次處於DLL初始化或分離例程 中。

的問題是,一個COM + DLL定稿從 DLL_THREAD_DETACH運行,這樣的類:TThread.WaitFor將等待 indefinitly因爲線程將不會終止,因爲它無法處理 其DLL_THREAD_DETACH直到最終完成。

更新

我已經花了好幾天找出原因,並有一個變通是手動執行AsyncCall單元的定稿部分。

這與算法無關,因爲即使非常簡單的代碼也會導致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; 
+1

你需要找到一種方式來重現它在一個簡單的例子,或花一些時間調試自己的代碼,以找出問題。我們無法爲您調試我們無法訪問的代碼,爲了提供代碼,我們可以使用您需要提供的MCVE。確定如何提供人的過程通常可以解決問題。 –

+4

你還需要花更多時間來嘗試在這裏發帖前自行解決問題。至少在最後一週,你每天至少要平均提出3個問題,這表明在發佈問題之前,很少有人努力解決問題(包括如何創建一個MCVE)。在你嘗試解決問題的其他方法已經耗盡之後,我們應該成爲「最後的手段」的地方。由於你最近發佈的許多問題都是重複的,這表明你很少或者根本沒有自己的研究。 –

+0

你有什麼'TThread.Synchronize'在你的線程/任務的要求?這是釋放線程的情況下死鎖的一個主要原因 –

回答

0

看起來你正在嘗試做一些你不允許在dll的初始化/終結中做的事情。

您需要從其他地方運行該終結代碼。通常這是在DLL導出的幫助函數內完成的。另一種選擇是在你的dll的最後一個COM對象被釋放時運行終結。

導出功能DllCanUnloadNow看起來像終止線程的候選人。