【问题标题】:DLL and class in multithreaded application多线程应用程序中的 DLL 和类
【发布时间】:2013-07-30 14:59:27
【问题描述】:

我的 DLL 中有一个类。而这个DLL提供了一个“接口”来创建这个类的对象并调用它们的方法。

类代码(简体):

TLogger = class
  private
    //
  public
    function AddToLog(sBuf: PWideChar): Word;
    constructor Create(Name: PWideChar);
    destructor Destroy; override;
end;

constructor Create(Name: PWideChar);
begin
  //
end; 

destructor TLogger.Destroy;
begin
  //
end;

function TLogger.AddToLog(sBuf: PWideChar): Word;
var
  Temp1 : WideString;
  Temp2 : AnsiString;

begin
  Result := NO_ERRORS;

  WaitForSingleObject( FMutex, INFINITE );

  Temp1 := 'a';
  Temp2 := Temp1;

  //

  ReleaseMutex( FMutex );
end;

DLL 代码

function CreateLogger( LogFileName: PWideChar; PLogger: PCardinal): Word; stdcall;
begin
  try
    PLogger^ := Cardinal(TLogger.Create(LogFileName));
    Result := NO_ERRORS;
  except
    Result := ERR_CREATE_OBJECT;
  end;
end;

function DestroyLogger( PLogger: Cardinal ): Word; stdcall;
begin
  try
    TLogger(PLogger).Free;
    Result := NO_ERRORS;
  except
    Result := ERR_OBJECT_NOT_FOUND;
  end;
end;

function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
  try
    Result := TLogger(PLogger).AddToLog( BufStr );
  except
    Result := ERR_OBJECT_NOT_FOUND;
  end;
end;

当我尝试从 1 个线程使用此库时 - 一切正常。当我创建许多线程调用函数AddToLog 时,问题就开始了(每个线程都有自己的类对象)。有一段时间我会赶上Access ViolationInvalid pointer operation。 我进行了一些研究并指出如果变量Temp2 具有WideString 类型,则一切正常。另一种解决方案是将互斥锁移到库代码中(它只是一个“研究”代码):

function AddToLog( PLogger: Cardinal; BufStr: PWideChar ): Word; stdcall;
begin
  WaitForSingleObject( TLogger(PLogger).FMutex, INFINITE );
  Result := TLogger(PLogger).AddToLog( BufStr );
  ReleaseMutex( TLogger(PLogger).FMutex );
end;

第二种解决方案对我不利,因为每个对象都有自己的互斥锁(想法是,如果两个对象必须与一个文件一起使用,则它们具有相同的互斥锁以相互等待;如果两个对象必须与不同的文件一起使用,则它们具有不同的互斥体并并行工作)。

我试图解决这个问题 2 天,但我不明白出了什么问题。字符串转换如何导致这样的问题?

【问题讨论】:

  • 在我看来您删除了太多代码。即使您删除了互斥锁,并且即使所有线程共享同一个记录器实例,您在此处拥有的代码也是线程安全的。你需要一个 SSCCE。
  • 你设置IsMultiThread := True;在 DLL 中?
  • @TOndrej 啊,我敢打赌就是这样。使用IsMultiThread 作为False,堆分配器将不是线程安全的。我建议您将其添加为答案。

标签: multithreading delphi class dll


【解决方案1】:

输入以下行:

IsMultiThread := True;

作为 DLL 项目主代码块的第一行。这将指示内存管理器切换到线程安全模式。

这可以解释AnsiStringWideString 之间的行为差​​异,因为AnsiString 是由Delphi 内存管理器分配的,而WideString 是在COM 堆上分配的。当IsMultiThreadFalse 时,Delphi 内存管理器不是线程安全的,但 COM 堆始终是线程安全的。

【讨论】:

  • +1 另请注意,WideString 是线程安全的,但也比 AnsiString 或 UnicodeString 慢得多。即使 BSTR 系统分配器的速度自 Windows Vista 以来确实提高了很多,相对于 XP 或 2K。
  • 这帮助我解决了一个困扰了 3 天的问题。我正在使用可能从不同线程调用我的回调的 COM 对象。由于神秘的竞争条件,我一直在追逐与数组(取消)分配、接口释放和错误引用计数相关的奇怪错误...我真的做到了不知道默认的 Delphi 内存管理器不是线程安全的,除非你启用它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多