在Delphi中从函数指针调用函数

4

我正在尝试在Delphi中构建一个通用的工作线程,可以将函数/过程(无论哪种)作为参数传递并让它执行。

我的想法是在TThread类中添加一个字段,并从TThread.Execute中调用它。

因此,线程外部的代码将会是:

  MyThread := TWorkerThread.Create(True);
  Mythread.CallBackF := @Foo;
  try
    MyThread.Resume;
  except
    MyThread.Free;
  end;

如何在TWorkerThread中保留@foo的引用并从Execute内部调用它?


1
以上代码非常糟糕。你在创建线程后立即释放它。 (是的,线程在...单独的线程中运行。也许你想要一个WaitFor?) - Andreas Rejbrand
谢谢您的回复,Andreas。我会进行编辑。说实话,我在Delphi中对线程没有太多经验。 - Ed.C
顺带一提,我不想等待线程。这就是 WaitFor 做的事情吗?我不需要任何决策返回,只需要网络例程,需要最多 500 毫秒。 - Ed.C
@Ed.C:是的,WaitFor方法会一直等待线程执行完毕才返回。你可能不想要这个。也许使用FreeOnTerminate属性可以解决问题? - Andreas Rejbrand
1
如果您的线程类的唯一预期用途是为其分配参数并运行,则不要费心让它以其他方式创建。该对象需要回调指针,因此将其作为构造函数的参数。它永远不应该挂起,所以不要将其作为参数。然后,您只需创建线程并忘记它:TWorkerThread.Create(Foo)。在构造函数中,调用inherited Create(False),设置CallBackF,并设置FreeOnTerminate := True - Rob Kennedy
@Andreas Rejbrand,TThread析构函数等待线程完成并进行消息泵处理,因此此代码是正确的(除非您想要执行异步操作)。 - Alex
3个回答

6

感谢您的回复@skamradt。这些库看起来非常不错。对于任何更大的项目,我可能会记住它们,但现在我要使用带参数的构造函数方法。这只是一个小应用程序,我已经完成了 - 现在只是进行优化。 :) - Ed.C

5
我并不是线程方面的专家,但我认为这样做应该可以解决问题:
interface

    type
      TProcRef = reference to procedure;
      TWorkerThread = class(TThread)
      public
        proc: TProcRef;
        procedure Execute; override;
        class procedure RunInThread(AProc: TProcRef);
      end;

implementation

procedure TWorkerThread.Execute;
begin
  inherited;
  proc;
end;

class procedure TWorkerThread.RunInThread(AProc: TProcRef);
begin
  with TWorkerThread.Create(true) do
  begin
    FreeOnTerminate := true;
    proc := AProc;
    Resume;
  end;
end;    

然后,如果您有任何类似于以下的过程:
procedure P;
begin
  while true do
  begin
    sleep(1000);
    beep;
  end;
end;

你只需要这样做

procedure TForm1.Button1Click(Sender: TObject);
begin
  TWorkerThread.RunInThread(P);
end;

您甚至可以进行以下操作

TWorkerThread.RunInThread(procedure begin while true do begin sleep(1000); beep; end; end);

+1 @Andreas Rejbrand,不错的代码,能否在这样的过程中添加参数?例如procedure P(AValue:String; AValue:Integer);? - XBasic3000
2
Ed,如果您必须在过程名称前面加上@,那通常意味着过程指针类型和您要传递给它的过程不匹配。确保签名完全匹配;不要尝试在期望过程的地方传递函数或反之亦然。@XBasic,是的,您可以添加任何想要的参数。只需确保将它们添加到类型声明中:TProcRef = reference to procedure(AValue: string; AValue2: Integer) - Rob Kennedy
@Rob Kennedy,非常感谢,这非常有用。 - XBasic3000
“TProcRef = reference to procedure”和“TProcRef = procedure”之间有什么区别,除了前者无法在早期版本中编译? - Sertac Akyuz

4

请看QueueUserWorkItem函数。

它可以在一个线程中执行任意函数,而无需创建新的线程。只需记得将IsMultithreaded全局变量切换为True即可。


1
"...不需要应用程序员创建一个",我猜? - Andreas Rejbrand
1
是的。(stackoverflow 不允许短评论,所以我写了这个...) - Alex
1
CreateThread 似乎更合适,因为根据 MSDN 的说法,QueueUserWorkItem 是为线程池设计的。 - Remko
"QueueUserWorkItem"看起来真不错!我甚至可以传递参数! - Ed.C
1
@Remko,使用线程池。这就是为什么这个函数很酷的原因。您可以安排工作项而不必担心线程、池、同步问题...作为内置线程池的前端-这是内部实现的描述,仅此而已。这意味着您必须公平竞争(例如,不要忘记WT_EXECUTELONGFUNCTION)。这并不意味着“不要使用此函数!” - Alex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接