Нарушение доступа к Delphi AsyncCalls

Я впечатлен этой библиотекой 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

Вы пытаетесь использовать версию переменных параметра AsyncCall. В коде говорится, что поддерживаются следующие типы:

Supported types:
  Integer      :  Arg: Integer
  Boolean      :  Arg: Boolean
  Char         :  Arg: AnsiChar
  WideChar     :  Arg: WideChar
  Int64        :  [const] Arg: Int64
  Extended     :  [const] Arg: Extended
  Currency     :  [const] Arg: Currency
  String       :  [const] Arg: ShortString
  Pointer      :  [const] Arg: Pointer
  PChar        :  [const] Arg: PChar
  Object       :  [const] Arg: TObject
  Class        :  [const] Arg: TClass
  AnsiString   :  [const] Arg: AnsiString
  UnicodeString:  [const] Arg: UnicodeString
  PWideChar    :  [const] Arg: PWideChar
  WideString   :  [const] Arg: WideString
  Interface    :  [const] Arg: IInterface
  Variant      :  const Arg: 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.

Обратите внимание, что я внесла следующие изменения:

  • Удалено использование Forms и вызов ProcessMessages.
  • Сделано GetMax используйте соглашение о вызове cdecl.
  • Передать max как указатель на целое число, а не на параметр var. Это связано с тем, что параметры var не поддерживаются.
  • Пусть GetMax использует глобальную переменную arr, а не принимает ее как параметр. Это было для целесообразности. Альтернативой может быть передача адреса первого элемента.

Честно говоря, ваш код достаточно сложный, чтобы переменный параметр AsyncCall чувствовал себя немного растянутым. Возможно, вам лучше передать объект или указатель на запись, а не перегружать переменные.


Я должен также указать, что AsyncCalls больше не разрабатывается. Вы, на мой взгляд, лучше обслуживали бы, принимая OTL, чем AsyncCalls.