簡體   English   中英

從TThread動態初始化和調用LoadLibrary僅需一次

[英]Dynamically initialize and call LoadLibrary from a TThread by demand only once

我有一個Delphi DLL,需要從主UI應用程序或輔助線程中調用它。

我不想每次調用DLL時都調用LoadLibrary / FreeLibrary。 但是,我也不想在應用程序初始化部分中加載它。 因為在應用程序的生存期內我可能根本不使用DLL。

因此,我需要的是第一個用於初始化和加載DLL的調用程序(線程或主UI)。 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 ,則DLLInitialized_OKDLLModule都沒有實際用途。

為了使事情更容易推理,您應該盡量避免使用最少的變量。 類似於以下內容的東西應該起作用:

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變量使用整數或枚舉,對於未初始化失敗成功 ,請使用不同的值。

您已經錯誤地實施了雙重檢查鎖定。 您可以先分配給DLLProc然后DLLModuleDLLProc 因此, DLLModule可以為非零值,而DLLProc仍為null。

所有初始化完成后,必須修改在鎖之外測試的變量。

模式是這樣的:

if not Initialised then begin
  Lock.Enter;
  if not Initialised then begin
    // Do initialisation
    Initialised := True; // after initialisation complete
  end;
  Lock.Leave;
end;

請記住,如此處實現的雙重檢查鎖定僅由於強大的x86內存模型而起作用。 如果您曾經將此代碼移到內存模型較弱的硬件上,將無法實現。 您需要實施障礙。 可以做,但並非完全無關緊要。

雙重檢查鎖定在這里毫無意義。 刪除它並用一個關鍵部分保護所有內容。 您正在旋轉線程,這是一項非常昂貴的任務。 關鍵部分的潛在爭用可以忽略不計。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM