根据需求动态初始化并从TThread调用LoadLibrary一次
我有一个Delphi DLL,需要从我的主UI应用程序或工作线程调用。
每次调用DLL时,我都不想调用LoadLibrary / FreeLibrary。 但是,我也不想在我的应用程序初始化部分加载它。 因为我可能在应用程序的生命周期中根本不使用DLL。
所以我需要的是第一个调用者(线程或主UI)来初始化和加载DLL。 该DLL将在最终化部分中卸载。 我意识到我需要一些同步。 所以我使用了关键部分,但我似乎无法使其工作。
只有一个线程应该尝试并加载DLL。 如果失败,其他线程不应该尝试一次又一次加载DLL。
同步未按预期工作 !
有人可以建议为什么?
MCVE:
program Project1;
{$APPTYPE CONSOLE}
uses
Windows,
SysUtils,
Classes;
const
MyDLL = 'MyDLL.dll';
type
TDLLProcessProc = function(A: Integer): Integer; stdcall;
var
DLLProc: TDLLProcessProc = nil;
DLLModule: HMODULE = 0;
DLLInitialized: Boolean = False;
DLLInitialized_OK: Boolean = False;
CS: TRTLCriticalSection;
procedure InitDLLByFirstCall;
begin
if DLLModule = 0 then
begin
if DLLInitialized then Exit;
EnterCriticalSection(CS);
try
if DLLInitialized then Exit;
DLLInitialized := True;
DLLModule := LoadLibrary(MyDLL);
if DLLModule = 0 then RaiseLastWin32Error;
DLLProc := GetProcAddress(DLLModule, 'Process');
if @DLLProc = nil then RaiseLastWin32Error;
DLLInitialized_OK := True;
finally
LeaveCriticalSection(CS);
end;
end;
end;
function DLLProcess(A: Integer): Integer;
begin
InitDLLByFirstCall;
if not DLLInitialized_OK then
raise Exception.Create('DLL was not initialized OK');
Result := DLLProc(A);
end;
type
TDLLThread = class(TThread)
private
FNum: Integer;
public
constructor Create(CreateSuspended: Boolean; ANum: Integer);
procedure Execute; override;
end;
constructor TDLLThread.Create(CreateSuspended: Boolean; ANum: Integer);
begin
FreeOnTerminate := True;
FNum := ANum;
inherited Create(CreateSuspended);
end;
procedure TDLLThread.Execute;
var
RetValue: Integer;
begin
try
RetValue := DLLProcess(FNum);
Sleep(0);
Writeln('TDLLThread Result=> ' + IntToStr(RetValue));
except
on E: Exception do
begin
Writeln('TDLLThread Error: ' + E.Message);
end;
end;
end;
var
I: Integer;
begin
InitializeCriticalSection(CS);
try
// First 10 thread always fail!
for I := 1 to 10 do
TDLLThread.Create(False, I);
Readln;
for I := 1 to 10 do
TDLLThread.Create(False, I);
Readln;
finally
DeleteCriticalSection(CS);
end;
end.
DLL:
library MyDLL;
uses
Windows;
{$R *.res}
function Process(A: Integer): Integer; stdcall;
begin
Result := A;
end;
exports
Process;
begin
IsMultiThread := True;
end.
你需要修改你的代码,使得在InitDLLByFirstCall
开始时检查的条件变量只有在所有初始化完成后才设置。 DLL句柄因此是一个不好的选择。
其次,您需要在关键部分的外部和内部使用相同的条件变量 - 如果您使用DLLInitialized
,那么DLLInitialized_OK
和DLLModule
都没有DLLModule
。
为了让事情更容易理解,你应该尝试摆脱最少数量的变量。 像下面的东西应该工作:
var
DLLProc: TDLLProcessProc = nil;
DLLInitialized: Boolean = False;
CS: TRTLCriticalSection;
procedure InitDLLByFirstCall;
var
DLLModule: HMODULE;
begin
if DLLInitialized then
Exit;
EnterCriticalSection(CS);
try
if not DLLInitialized then
try
DLLModule := LoadLibrary(MyDLL);
Win32Check(DLLModule <> 0);
DLLProc := GetProcAddress(DLLModule, 'Process');
Win32Check(Assigned(DLLProc));
finally
DLLInitialized := True;
end;
finally
LeaveCriticalSection(CS);
end;
end;
function DLLProcess(A: Integer): Integer;
begin
InitDLLByFirstCall;
if @DLLProc = nil then
raise Exception.Create('DLL was not initialized OK');
Result := DLLProc(A);
end;
如果你不想检查DLLProcess
里面的函数地址,那么你也可以为DLLInitialized
变量使用一个整数或枚举,不同的值没有初始化,失败和成功。
你有你的双重锁定实施不正确。 在分配给DLLModule
之前分配给DLLProc
。 所以DLLModule
可以是非零的,而DLLProc
仍然是空的。
在所有初始化完成后,您在锁外测试的变量必须进行修改。
模式是这样的:
if not Initialised then begin
Lock.Enter;
if not Initialised then begin
// Do initialisation
Initialised := True; // after initialisation complete
end;
Lock.Leave;
end;
请记住,这里实现的双重检查锁定仅适用于强大的x86内存模型。 如果您将这些代码移到硬内存模型的硬件上,它将不会像实现一样工作。 你需要实施障碍。 可能做,但不是完全微不足道。
虽然双重检查锁定是毫无意义的。 删除它并用一个关键部分保护所有内容。 你正在旋转一个线程,一个非常昂贵的任务。 关键部分的潜在争用可以忽略不计。
链接地址: http://www.djcxy.com/p/91037.html上一篇: Dynamically initialize and call LoadLibrary from a TThread by demand only once