13

I would like to have a thread running in background which will check connection to some server with given time interval. For example for every 5 seconds.

I don't know if there is a good "desing pattern" for this? If I remember corretly, I've read somewehere that sleeping thread in its execute method is not good. But I might be wrong.

Also, I could use normal TThread class or OTL threading library.

Any ideas?

Thanks.

mghie
  • 32,028
  • 6
  • 87
  • 129
Wodzu
  • 6,932
  • 10
  • 65
  • 105

5 Answers5

16

In OmniThreadLibrary, you would do:

uses
  OtlTask,
  OtlTaskControl;

type
  TTimedTask = class(TOmniWorker)
  public
    procedure Timer1;
  end;

var
  FTask: IOmniTaskControl;

procedure StartTaskClick;
begin
  FTask := CreateTask(TTimedTask.Create())
    .SetTimer(1, 5*1000, @TTimedTask.Timer1)
    .Run;
end;

procedure StopTaskClick;
begin
  FTask.Terminate;
  FTask := nil;
end;

procedure TTimedTask.Timer1;
begin
  // this is triggered every 5 seconds
end;

As for sleeping in Execute - it depends on how you do it. If you use Sleep, then this might not be very wise (for example because it would prevent the thread to stop during the sleep). Sleeping with WaitForSingleObject is fine.

An example of TThread and WaitForSingleObject:

type
  TTimedThread = class(TThread)
  public
    procedure Execute; override;
  end;

var
  FStopThread: THandle;
  FThread: TTimedThread;

procedure StartTaskClick(Sender: TObject);
begin
  FStopThread := CreateEvent(nil, false, false, nil);
  FThread := TTimedThread.Create;
end;

procedure StopTaskClick(Sender: TObject);
begin
  SetEvent(FStopThread);
  FThread.Terminate;
  FThread.Free;
  CloseHandle(FStopThread);
end;

{ TTimedThread }

procedure TTimedThread.Execute;
begin
  while WaitForSingleObject(Form71.FStopThread, 5*1000) = WAIT_TIMEOUT do begin
    // this is triggered every 5 seconds
  end;
end;

OTL timer implementation is similar to the TThread code above. OTL timers are kept in priority list (basically the timers are sorted on the "next occurence" time) and internal MsgWaitForMultipleObjects dispatcher in TOmniWorker specifies the appropriate timeout value for the highest-priority timer.

gabr
  • 26,580
  • 9
  • 75
  • 141
  • Thanks gabr, I wanted to ask you about this on your forum but it does not work. +1 – Wodzu Dec 07 '11 at 09:15
  • Yes, something is wrong with the domain server; I'm working on that. – gabr Dec 07 '11 at 09:19
  • Could you tell us more how did you implement your timing solution? Perhaps write a blog post about it?:) – Wodzu Dec 07 '11 at 09:21
  • thank you. I will delve for more details in your forum when it will be running. – Wodzu Dec 07 '11 at 10:26
  • Events in what whay, David? Windows events? If you write your own thread loop then you can use whatever you want, but you can also opt out of it and leave OTL to handle thread loop by itself. – gabr Dec 07 '11 at 17:04
14

You could use an event and implement the Execute method of the TThread descendant by a loop with WaitForSingleObject waiting for the event, specifying the timeout. That way you can wake the thread up immediately when needed, e.g. when terminating.

Ondrej Kelle
  • 36,941
  • 2
  • 65
  • 128
1

If the thread runs for the life of the app, can be simply terminated by the OS on app close and does not need accurate timing, why bother with solutions that require more typing than sleep(5000)?

Martin James
  • 24,453
  • 3
  • 36
  • 60
  • Destructor of the TThread cals WaitFor internaly. That's why code like "procedure TForm1.FormDestroy(Sender: TObject); begin MyThread.Free; end;" will cause a hang for 5 seconds. – Torbins Dec 07 '11 at 10:31
  • 4
    Because then you can wait for up to 5 seconds for the thread (and by that, the program) to terminate. – gabr Dec 07 '11 at 10:31
  • 1
    Me, I simply sleep threads for 1000ms, and it wakes up, checks if Terminated, and then checks for work to do. The thread is pretty much idle on the system, and it works well and efficiently. Sure, there is a 1sec delay on closedown, but I call the Terminate on all threads first, and then do the WaitFors, so the total delay on all threads is 1sec, and not 1sec times the thread count. Since some threads will be working for longer, the cost is negligible. – mj2008 Dec 07 '11 at 12:30
  • 1
    @Torbins etc. - if you don't destroy the thread, the dtor will not be called and there will be no wait! – Martin James Dec 07 '11 at 13:20
  • That will trigger memory leak alert. – Torbins Dec 07 '11 at 13:23
  • 2
    @gabr you can wait, sure, but why would you want to? Don't you want your app to shut down immediately? – Martin James Dec 07 '11 at 13:23
  • @Martin, you misread me. If the thread calls Sleep(5000) and app then calls Thread.Terminate, thread will take up to 5 seconds to react to terminate and your app will be blocked during this time. – gabr Dec 07 '11 at 13:25
  • @Torbins - a memory leak alert, maybe, an actual leak, no. The process is terminating - the OS will ensure there is no leak. If the memory manager issues an exception or other alert on process termination, that as a bug in the memory manager. If the choice is between supressing a spurious error message and wasting time/effort on terminating threads that are about to be terminated by the OS anyway, I'll take the easy path. Which memory manager issues the spurious alert - I don't get any alert with D2009? – Martin James Dec 07 '11 at 13:30
  • 1
    TThread.Terminate just sets a flag, AFAIK, so cannot cause any delay. What does cause a delay is TThread.WaitFor, either explicitly or as called from TThread.Destroy. If you don't call TThread.WaitFor, your app will not be blocked. – Martin James Dec 07 '11 at 13:36
  • I'm with Martin on this one. I don't bother terminating, destroying, or waiting for threads if they can be interrupted without harm at shutdown. – Marcus Adams Dec 07 '11 at 18:13
1

To add another means of achieving a 5-sec event it is possible to use the Multimedia Timer which is similar to TTimer but has no dependence on your application. After configuring it (you can setup one-shot or repetitive) it calls you back in another thread. By its nature it is very accurate (to within better than 1ms). See some sample Delphi code here.

The code to call the timer is simple and it is supported on all Windows platforms.

Brian Frost
  • 13,334
  • 11
  • 80
  • 154
0

Use CreateWaitableTimer and SetWaitableTimer

MishaU
  • 708
  • 6
  • 14
  • 4
    Why is this better/worse than the other solutions discussed here? (IOW, you should improve your answer). – gabr Dec 07 '11 at 09:36