multithreading - 仅按需从 TThread 动态初始化和调用 LoadLibrary 一次

标签 multithreading delphi dll synchronization delphi-7

我有一个 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. 

动态链接库:

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

为了让事情更容易推理,您应该尝试使用最少数量的变量。像下面这样的东西应该可以工作:

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 变量使用整数或枚举,为未初始化失败成功

关于multithreading - 仅按需从 TThread 动态初始化和调用 LoadLibrary 一次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34401028/

相关文章:

c# - 如何安全地使用在任意线程上进行回调的库

windows - Delphi DLL 问题 - 接收到错误的整数值和访问冲突问题

delphi - Delphi 2009找不到包中声明的记录

delphi - System.IOUtils 中的 TPath 记录在哪里初始化?

c++ - 不同编译器的 DLL 兼容性

C# 与 C++ DLL 调用

c++ - 使用 node.js 访问以 c++ 编写的 SDK

c# - .NET 中的多线程绘图?

c++ - std::weak_ptr::锁和对象销毁

Python脚本长执行函数