问题
What I basically want is to start AsyncCall and proceed with my code loading. I have Interface section that consumes lots of time (600+ms) and I want to load this code in independent thread.
I've tried to use AsyncCall
to make something like this:
procedure Load;
begin
...
end;
initialization
AsyncCall(@Load, []); // or LocalAsyncCall(@Load)
However, this Load
procedure actually starts in Main thread and not in the new created thread. How can I force the Load
procedure to be loaded in any thread other than MainThread
?
I can create TThread
and Execute
this but I want to force AsyncCall
or LocalAsyncCall
or anything from AsyncCall
library to make to work.
Thanks for your help.
回答1:
The problem is that your code is not retaining the IAsyncCall
interface that is returned by the AsyncCall
function.
AsyncCall(@Load, []);
//AsyncCall returns an IAsyncCall interface,
//but this code does not take a reference to it
Because of this, the interface that is returned has its reference count decremented to zero as soon as the initialization section completes. This therefore frees the object that implements the interface which does this:
destructor TAsyncCall.Destroy;
begin
if FCall <> nil then
begin
try
--> FCall.Sync; // throw raised exceptions here
finally
FCall.Free;
end;
end;
inherited Destroy;
end;
The key line is the call to Sync
which forces the asynchronous call to be executed to completion. All this happens in the main thread which explains the behaviour that you report.
The solution is that you simply need to keep the IAsyncCall
interface alive by storing it in a variable.
var
a: IAsyncCall;
initialization
a := AsyncCall(@Load, []);
In the real code you need to ensure that Load
had completed before running any code that is reliant on Load
. When your program reached a point where it required Load
to have been called it has to call Sync
on the IAsyncCall
interface.
So you might write it something like this.
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.
The call EnsureLoaded
from other units that required Load
to have run. Or, alternatively, call EnsureLoaded
from any methods exported by MyUnit
that depended on Load
having run. The latter option has much better encapsulation.
回答2:
Have you tried something like this?:
procedure Load;
begin
if GetCurrentThreadId <> MainThreadID then
Beep;
end;
var a: IAsyncCall;
initialization
a := AsyncCall(@Load, []);
a.ForceDifferentThread;
ForceDifferentThread() tells AsyncCalls that the assigned function must not be executed in the current thread.
来源:https://stackoverflow.com/questions/8809171/asynccall-with-delphi-2007