Delphi异步调用引发访问冲突

10

我对Andy开发的这个AsynCalls库印象深刻。

我写了一段代码来测试这个库,但是它总是得到内存A/V,我在这里错过了什么吗?

以下代码旨在使用两个异步线程并行执行此简单任务(从数组中获取最大值)。

program Test;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math,
  Windows,
  Forms,
  AsyncCalls in 'AsyncCalls.pas';

var
  arr: array of integer;
  i: integer;

procedure GetMax(const arr: array of integer; left, right: integer; var max: integer);
var
  i: integer;
begin
  max := arr[left];
  for i := left + 1 to right do
  begin
    if (arr[i] > max) then
    begin
      max := arr[i];
    end;
  end;
end;

const
  N = 100000;

var
  a, b: IAsyncCall;
  maxv, max1, max2: integer;

begin
  SetLength(arr, N);
  maxv := -1;
  for i := 0 to High(arr) do
  begin
    arr[i] := RandomRange(0, MaxInt);
    if (arr[i] > maxv) then
    begin
      maxv := arr[i];
    end;
  end;

  a := AsyncCall(@GetMax, [arr, 0, Length(arr) div 2, max1]);
  b := AsyncCall(@GetMax, [arr, (Length(arr) div 2) + 1, High(arr), max2]);
  while (AsyncMultiSync([a, b], True, 10) = WAIT_TIMEOUT) do
  begin
    Application.ProcessMessages;
  end;
  Writeln(max1, ', ', max2, ', ', Max(max1, max2));
  Writeln(maxv);
  Readln;
end.

1
你为什么在控制台应用程序中调用 ProcessMessages - David Heffernan
无论我是否删除它,仍会导致A/V问题。 - justyy
3
感谢您提供的优秀的SSCCE示例代码! - David Heffernan
1个回答

8
你正在尝试使用带变量参数的 AsyncCall 版本。代码中支持以下类型:
支持的类型:
  整数        :  参数:整数
  布尔值      :  参数:布尔值
  字符        :  参数:AnsiChar
  宽字符      :  参数:WideChar
  Int64       :  [const] 参数:Int64
  扩展        :  [const] 参数:Extended
  货币        :  [const] 参数:Currency
  字符串      :  [const] 参数:ShortString
  指针        :  [const] 参数:Pointer
  PChar       :  [const] 参数:PChar
  对象        :  [const] 参数:TObject
  类          :  [const] 参数:TClass
  Ansi字符串 :  [const] 参数:AnsiString
  Unicode字符串: [const] 参数:UnicodeString
  PWideChar   :  [const] 参数:PWideChar
  WideString  :  [const] 参数:WideString
  接口        :  [const] 参数:IInterface
  变体        :  const 参数:Variant
你的函数接收一个开放数组参数和一个 var 参数,但以上列表中没有这两种类型。此外,该函数必须使用 cdecl 调用约定。
下面是一个可行的代码版本:
program Test;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  Math,
  AsyncCalls in 'AsyncCalls.pas';

var
  arr: array of integer;

procedure GetMax(left, right: integer; max: PInteger); cdecl;
var
  i: integer;
begin
  max^ := arr[left];
  for i := left + 1 to right do
  begin
    if (arr[i] > max^) then
    begin
      max^ := arr[i];
    end;
  end;
end;

const
  N = 100000;

var
  a, b: IAsyncCall;
  i, maxv, max1, max2: integer;

begin
  SetLength(arr, N);
  maxv := -1;
  for i := 0 to High(arr) do
  begin
    arr[i] := RandomRange(0, MaxInt);
    if (arr[i] > maxv) then
    begin
      maxv := arr[i];
    end;
  end;
  a := AsyncCall(@GetMax, [0, Length(arr) div 2, @max1]);
  b := AsyncCall(@GetMax, [(Length(arr) div 2) + 1, High(arr), @max2]);
  AsyncMultiSync([a, b], True, INFINITE);
  Writeln(max1, ', ', max2, ', ', Max(max1, max2));
  Writeln(maxv);
  Readln;
end.

请注意我做了以下更改:
  1. 删除了使用 Forms 和调用 ProcessMessages
  2. 使 GetMax 使用 cdecl 调用约定。
  3. max 作为指向整数的指针传递,而不是作为 var 参数。因为不支持 var 参数。
  4. GetMax 使用全局变量 arr,而不是将其作为参数接收。这是为了方便起见。另一种选择可能是传递第一个元素的地址。
坦白地说,您的代码已经足够复杂,以至于变量参数 AsyncCall 感觉有点勉强。与其使用变量参数重载,您最好传递对象或指向记录的指针。
我还要指出的是,AsyncCalls 已经不再更新。在我看来,您最好采用 OTL 而不是 AsyncCalls。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接