调用 FreeLibrary 时可能出现死锁
Posted
技术标签:
【中文标题】调用 FreeLibrary 时可能出现死锁【英文标题】:Possible Deadlock on Calling FreeLibrary 【发布时间】:2017-04-21 07:29:47 【问题描述】:我必须在 Delphi XE7 中编写一个 DLL。我想在 DLL 中使用 TParallel.For。 DLL 被加载到 C++ 应用程序中,一切正常。但是,当应用程序终止或调用 FreeLibrary 时,应用程序会挂起。如果我删除所有 TParallel.For 循环并用标准循环替换它们,应用程序将正常退出。
TParallel.For 循环非常简单:
TParallel.For(0, inImage.Height -1,
Procedure(ty : integer)
begin
SomeProcedure(ty);
end);
如果我用完全相同的代码创建一个 Delphi 应用程序,一切都会完美运行。
经过大量研究和调试,看起来有一个死锁阻止 C++ 应用程序在调用 FreeLibrary 时退出,但我找不到 TParallel 中的问题所在。
简单总结一下情况:
TParallel.For 循环全部完成并产生正确的结果。 Delphi .exe 中完全相同的 TParallel.For 代码可以正常工作。 DLL 已加载,函数已在 C++ 应用程序中正确调用和执行。 如果没有 TParallel.For 循环,C++ 应用程序将正确退出。 如果存在 TParallel.For 循环,C++ 应用程序将挂起。 我猜测调用 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.
在 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
回复非常“有帮助”的cmets,“代码有缺陷”和“自己调试”,谢谢,这是我做了太久的事情。因此,如果这里没有任何帮助,我将尝试获得切换到 OTL 的许可,这在相关 DLL 中确实有效。
更新 2
OTL 完全按预期工作。所以,是的,存在“代码缺陷”。我放弃。我建议完全放弃 Delphi,然后我们可以将所有内容转移到 C++ 和 C#。这必须是一个更好的短期(和长期)解决方案。
【问题讨论】:
我怀疑TParallel
创建了一个线程池并且需要一些明确的清理来阻止它们。 Delphi 可执行文件知道这种行为并会执行清理,而 C++ 可执行文件则不会。
代码有缺陷。只有你有代码。没有minimal reproducible example,我们只能猜测。不要让我们猜测。要么生成minimal reproducible example,要么自己做一些调试。
没有必要放弃和指责试图提供帮助的人。看来您有一个很好的 QP 报告候选人。请做到这一点,并加入 g+ Delphi 开发人员组并在那里报告。 David M 在那里很活跃,可以提供帮助。
类似的错误报告:FreeLibrary hangs when a TParallel.For is called within the DLL。我认为这是真正的问题:TThreadPool worker thread holds reference to last executed task
【参考方案1】:
虽然我使用的是 Delphi 10.0 Seattle,并且使用 Delphi EXE 加载了 Delphi DLL,但我也遇到过类似的问题。
无论如何,我想出的解决方案如下:
在您的 Delphi DLL 中,首先创建您自己的线程池。
使用 TParallel.For 的重载版本,它将线程池对象作为其最后一个参数,并提供您自己的线程池对象。
在卸载 Delphi DLL 之前,请确保释放线程池对象。
这种方法为我解决了这个问题。
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;
【讨论】:
以上是关于调用 FreeLibrary 时可能出现死锁的主要内容,如果未能解决你的问题,请参考以下文章