views:

121

answers:

3

I'm trying to build a generic worker thread in Delphi, one that I can pass a function/procedure (doesn't matter) as an argument and let it execute.

My guess is to add a field in the TThread class and call it from TThread.Execute.

So the code outside the thread is gonna be:

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

How do I keep a reference of @foo in the TWorkerThread and call it from inside Execute?

+3  A: 

I do not pretend to be an expert on threading, but I think this will do it:

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;    

Then, if you got any procedure, like

procedure P;
begin
  while true do
  begin
    sleep(1000);
    beep;
  end;
end;

you can just do

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

You can even do

TWorkerThread.RunInThread(procedure begin while true do begin sleep(1000); beep; end; end);
Andreas Rejbrand
The code above works for me in Delphi 2009.
Andreas Rejbrand
+1 @Andreas Rejbrand, nice Code, is it posible to add parameter on such procedure? like procedure P(AValue:String; AValue:Integer);?
XBasic3000
Ed, if you have to put `@` in front of a procedure name, that's usually an indication that the procedure-pointer type and the procedure you're trying to pass to it do not match. Make sure the signatures match exactly; do not try to pass a function where a procedure is expected or vice versa. @XBasic, yes, you can add whatever parameters you want. Just make sure you add them to the type declaration, too: `TProcRef = reference to procedure(AValue: string; AValue2: Integer)`
Rob Kennedy
@Rob Kennedy, Thank you very much its very useful.
XBasic3000
Is there a difference between `TProcRef = reference to procedure` and `TProcRef = procedure`, except that the former does not compile with earlier versions?
Sertac Akyuz
+4  A: 

Also, a good start into using generic threads would be AsyncCalls or Omni Thread Library.

skamradt
Thank you for your reply @skamradt. They really look nice libraries. I might keep them in mind for any bigger projects, but for now I'm going for the constructor with argument method. It's just a small app really, I'm already finished - just going through optimizations now. :)
Ed.C
+3  A: 

Take a look at QueueUserWorkItem function.

It executes arbitrary function in thread, without requiring you to create one.

Alexander
"... without *the application programmer* requiring to create one", I suppose?
Andreas Rejbrand
Yep. (stackoverflow doesn't allow short comments, so I write this...)
Alexander
CreateThread seems more appropriate since according to msdn QueueUserWorkItem is meant for Thread Pools
Remko
@Alexander, `QueueUserWorkItem` looks really neat! I can even pass arguments with that!
Ed.C
@Remko, *uses* thread pool. That's why this function is cool. You can shedule work item without worrying about threads, pools, sync issues... Being front-end of built-in thread pool - it's description of internal implementation, that's all. Which means that you must play fair (for example, don't forget about WT_EXECUTELONGFUNCTION). It's not a sign "don't use this function!".
Alexander