AsyncCall с Delphi 2007 - PullRequest
       22

AsyncCall с Delphi 2007

11 голосов
/ 10 января 2012

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

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

procedure Load;
begin
...
end;

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

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

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

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

Ответы [ 2 ]

8 голосов
/ 11 января 2012

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

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

var a: IAsyncCall;

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

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

5 голосов
/ 11 января 2012

Проблема в том, что ваш код не сохраняет интерфейс 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.Последний вариант имеет гораздо лучшую инкапсуляцию.

...