AsyncCall с Delphi 2007

В основном я хочу запустить AsyncCall и продолжить загрузку кода. У меня есть раздел интерфейса, который потребляет много времени (600 + мс), и я хочу загрузить этот код в независимый поток.

Я попытался использовать AsyncCall, чтобы сделать что-то вроде этого:

procedure Load;
begin
...
end;

initialization
  AsyncCall(@Load, []); // or LocalAsyncCall(@Load)

Однако эта процедура Load фактически начинается в основном потоке, а не в новом созданном потоке. Как я могу заставить процедуру Load загружаться в любой поток, отличный от MainThread?

Я могу создать TThread и Execute, но я хочу заставить AsyncCall или LocalAsyncCall или что-нибудь из библиотеки AsyncCall сделать работу.

Спасибо за вашу помощь.

Ответ 1

Проблема заключается в том, что ваш код не сохраняет интерфейс IAsyncCall, который возвращается функцией AsyncCall.

AsyncCall(@Load, []);
//AsyncCall returns an IAsyncCall interface,
//but this code does not take a reference to it

Из-за этого возвращаемый интерфейс имеет счетчик ссылок, уменьшенный до нуля, как только завершается раздел инициализации. Поэтому это освобождает объект, который реализует интерфейс, который делает это:

destructor TAsyncCall.Destroy;
begin
  if FCall <> nil then
  begin
    try
-->   FCall.Sync; // throw raised exceptions here
    finally
      FCall.Free;
    end;
  end;
  inherited Destroy;
end;

Ключевой линией является вызов Sync, который заставляет выполнить асинхронный вызов для завершения. Все это происходит в основном потоке, который объясняет поведение, о котором вы сообщаете.


Решение состоит в том, что вам просто нужно поддерживать интерфейс IAsyncCall, сохраняя его в переменной.

var
  a: IAsyncCall;

initialization
  a := AsyncCall(@Load, []);

В реальном коде вам нужно убедиться, что Load был завершен до запуска любого кода, который зависит от Load. Когда ваша программа достигла точки, в которой требовалось Load для вызова, она должна вызвать Sync в интерфейсе IAsyncCall.

Итак, вы можете написать что-то вроде этого.

unit MyUnit;

interface

procedure EnsureLoaded;

implementation

uses
  AsyncCalls;

....

procedure Load;
begin
  ....
end;

var
  LoadAsyncCall: IAsyncCall;

procedure EnsureLoaded;
begin
  LoadAsyncCall := nil;//this will effect a call to Sync
end;

initialization
  LoadAsyncCall := AsyncCall(@Load, []);

end.

Вызов EnsureLoaded из других единиц, для выполнения которого требуется Load. Или, альтернативно, вызовите EnsureLoaded из любых методов, экспортированных с помощью MyUnit, которые зависели от запуска Load. Последний вариант имеет гораздо лучшую инкапсуляцию.

Ответ 2

Вы пробовали что-то вроде этого?

procedure Load;
begin
  if GetCurrentThreadId <> MainThreadID then
    Beep;
end;

var a: IAsyncCall;

initialization
  a := AsyncCall(@Load, []);
  a.ForceDifferentThread;

ForceDifferentThread() сообщает AsyncCalls, что назначенная функция должна       не выполняются в текущем потоке.