根据需求动态初始化并从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_OKDLLModule都没有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

下一篇: Diagnosing memory leaks in Windows with UMDH