【问题标题】:AsyncCall with Delphi 2007Delphi 2007 的异步调用
【发布时间】:2012-01-10 19:18:21
【问题描述】:

我基本上想要的是启动AsyncCall 并继续我的代码加载。我有消耗大量时间(600+ms)的接口部分,我想在独立线程中加载此代码。

我尝试使用AsyncCall 来制作这样的东西:

procedure Load;
begin
...
end;

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

然而,这个Load 过程实际上是在主线程中开始的,而不是在新创建的线程中。如何强制将Load 过程加载到MainThread 以外的任何线程中?

我可以创建 TThreadExecute 这个,但我想强制 AsyncCallLocalAsyncCallAsyncCall 库中的任何内容使其工作。

感谢您的帮助。

【问题讨论】:

  • 不。不工作。即使我把它放在初始化之外,而是在一些虚拟按钮上加载过程。

标签: multithreading delphi asynchronous delphi-2007


【解决方案1】:

你有没有尝试过这样的事情?:

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

var a: IAsyncCall;

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

ForceDifferentThread() 告诉 AsyncCalls 分配的函数必须 不在当前线程中执行。

【讨论】:

  • 我试过了,但没有帮助。产生相同的结果 - 在主代码中而不是在单独的线程中工作。
  • @Ivan 你能展示一个最小的复制品吗?当我在我的代码中尝试 ForceDifferentThread 时,它确实在不同的线程中运行了 asynch 方法。
  • 事实上,我发现在我的测试用例中,只需引用AsyncCall 返回的IAsyncCall 就足以让代码远离主线程。
  • 我已将 A var 设为全局变量,现在可以使用了。实际上它现在可以在没有 ForceDifferentThread 的情况下工作(!!)感谢 Davi 和 Kobik 的帮助
【解决方案2】:

问题是您的代码没有保留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 的程度时,它必须在IAsyncCall 接口上调用Sync

所以你可以这样写。

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.

来自需要Load 运行的其他单位的呼叫EnsureLoaded。或者,或者,从MyUnit 导出的依赖于Load 运行的任何方法调用EnsureLoaded。后一种选择具有更好的封装性。

【讨论】:

  • 我试过了,你是对的。但是尝试在该条件行上设置刹车点,您会看到单击 button1 或 button2 您将收到相同的线程 ID - 它从同一个线程进入 MyProc。当我在我的主代码(不是测试应用程序)中这样做时,我看到加载过程是在主线程中执行的,而不是在其他线程中。此外,我不能将任何睡眠放入初始化部分,因为该部分应该以最快的速度工作。但即使我说它是一样的.. :(
  • @Ivan 显然我并不是说Sleep 是答案。 Sleep 绝不是任何问题的答案!我只是想解释发生了什么并给你一些线索。无论如何,我很确定 kobik 已经为您指出了解决方案。
  • 当然,感谢您的帮助和努力。不幸的是,无论我做什么,我都会收到相同的结果,并且代码总是在 MainThread 中执行。我担心 Delphi 版本不好,并且 AnsycCall 库适用于 2009 及更高版本。明天在办公室我将尝试使用最新的 Delphi 版本并消除这种可能性。如果没有,那么我将创建自己的线程。
  • @Ivan Andreas 编写了这段代码是一个超级明星。我确信它在 D2007 中有效,他说它有效。他说它甚至适用于 D5。如果您可以发布一个不起作用的最小样本,我相信我们可以找到解决方案。但是,是的,单独的 TThread 也很容易!
  • @David 你的答案是正确的。如果接口的范围消失,他将需要全局 IAsyncCall 接口不立即同步。永远不要在不存储接口的情况下调用 AsyncCalls 函数,这一点非常重要。最坏的情况是编译器必须生成 1 个临时接口变量的循环。循环将按顺序执行调用,因为每次迭代只有 1 个接口被重新分配。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 1970-01-01
相关资源
最近更新 更多