我的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 Violation
或Invalid 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天,但我无法理解出了什么问题。字符串转换如何导致此类问题?
答案 0 :(得分:14)
放下以下一行:
IsMultiThread := True;
作为DLL项目主代码块中的第一行。这将指示内存管理器切换到线程安全模式。
这可以解释AnsiString
和WideString
之间的行为差异,因为AnsiString
由Delphi内存管理器分配,WideString
在COM堆上分配。当IsMultiThread
为False
时,Delphi内存管理器不是线程安全的,但COM堆始终是线程安全的。