9

我有一个 dll 和一个用 Delphi 编写的测试应用程序。测试应用程序使用多个线程调用dll导出的函数。导出的函数有一个简单的线程安全实现。运行测试应用程序时会发生各种错误(访问冲突、无效指针操作、堆栈溢出等)或应用程序冻结。在某些情况下,应用程序完成时不会出现错误。

请注意,这些错误仅在使用多个线程时发生(表面)。只有从主线程调用函数时,一切正常。

我发现将 ShareMem 添加到 dll 和应用程序可以阻止所有这些类型的错误。但我不明白为什么。据我所知,只有在 dll 和应用程序之间传递长字符串时才需要 ShareMem。据我所知 WideString 不是一个长字符串。

同样根据这篇文章 ShareMem 不应该是必需的: 为什么 Delphi DLLs 可以使用 WideString 而不使用 ShareMem?

这是dll的来源:

library External;

uses
  Winapi.Windows;

type
  TMyType = class
  private
    FText: string;
  end;

function DoSomething(input: WideString; out output: WideString): Bool; stdcall;
var
  x: TObject;
begin
  x := TMyType.Create;
  try
    output := x.ClassName;
  finally
    x.Free;
  end;
  Result := True;
end;

exports
  DoSomething;
begin
end.

这是测试应用程序:

program ConsoleTest;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,
  Winapi.Windows,
  OtlParallel;

function DoSomething(input: WideString; out output: WideString): Bool; stdcall; external 'External.dll' name 'DoSomething';

var
  sResult: WideString;

begin
  try
    Parallel.&For(0, 500).Execute(procedure(value: Integer)
    var
      sResult: WideString;
    begin
      DoSomething('hhh', sResult);
    end);
    WriteLn('Done');
    ReadLn;
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

为什么 ShareMem 可以消除这些错误,还有其他方法可以修复这些错误吗?

我正在使用 Delphi XE2 和 OmniThread 3.07.5。

更新 - 从 VCL 应用程序的按钮的单击事件处理程序运行时出现相同问题 - 如果 DoSomething 在内部使用关键部分,则运行良好 - 如果从 TMyClass 中删除 FText 字段,则不会报告错误,但应用程序会随机冻结

4

1 回答 1

9

对于支持多线程的标准内存管理器(FastMM),您需要设置该IsMultiThread标志。

当您使用 RTL 进行线程处理时,会自动设置此标志。正如对该问题的评论所揭示的,OTL 也使用 RTL 来启动其线程。因此,可执行文件中的内存管理器知道线程,但 dll 中不同的内存管理器会导致错误。当您使用“sharemem”时,由于 OTL,只有一个内存管理器知道线程,因此您不会遇到任何错误。

除了使用共享内存管理器之外,另一种解决方案是为 dll 中的内存管理器设置标志。

于 2018-03-19T20:36:31.507 回答