Question

I would like to refresh a Treenode.text (with the elapsed time) and use a timer from the same thread. As i read its not recommend to use TTimer* in a thread, but then, what to use instead? Thank You.

*I want to use some timing instead of sleep(). Not just because thats cheesy(?), but i have to, because otherwise i can refresh treenodetext only in every sec.) >>

unit master_slave

...

Tsrch_slave_thread = class(TThread)   
protected
  procedure Execute; override;
public
  master: TMaster;         
end;

TMaster = class(TObject)
private
  ...
  FMasterNode: TTreeview; 
  Fsw: TStopWatch;
  Fslave_search_thread : Tsrch_slave_thread;
  ...
end;

...

implementation

...

procedure Tsrch_slave_thread.Execute;
var
  searchactive: integer;
begin
  while not terminated do begin
    searchactive := master.CMD_LISTCNT 
    if searchactive = 1 then //(master.CMD_LISTCNT's return value = 1, if master finished search on the bus)
      exit;
    sleep(1000); //dont want to flood the master with UDP packets... (master.CMD_LISTCNT sends UDP packets)
    synchronize(procedure begin
      with FmasterNode do  
        text := text + floattostr(Fsw.ElapsedMilliseconds / 1000);
    end);
  end;
end;
Was it helpful?

Solution

Instead of Sleep(1000), use a waitable event.

For example TSimpleEvent.

FMySimpleEvent.WaitFor(1000);

If you want to exit the thread early, override the TThread.DoTerminate method and call:

FMySimpleEvent.SetEvent;

Something like this:

procedure Tsrch_slave_thread.Execute;
var
  searchactive: integer;
begin
  while not terminated do begin
    searchactive := master.CMD_LISTCNT 
    if searchactive = 1 then (master.CMD_LISTCNT's return value = 1, if search finished)
      exit;
    if (FMySimpleEvent.WaitFor(1000) = wrTimeOut) then
      synchronize(procedure begin
        with FmasterNode do  
          text := text + floattostr(Fsw.ElapsedMilliseconds / 1000);
      end);
  end;
end;

procedure Tsrch_slave_thread.DoTerminate;
begin
  Inherited;
  FMySimpleEvent.SetEvent;
end;

OTHER TIPS

You can use a timer in a thread, just not a TTimer. You have to make your own based on SetTimer. And you also need to make your own window to process the messages because you cannot use AllocateHWnd in a thread. Now, if you know that you won't show any modal dialogs or run any modal sizing loops or modal menu loops, then you could use a windowless timer.

But all this forces you to implement a message loop in your thread. Which adds another layer of complexity. Implementing a timer in a background thread is one of the few scenarios where a blocking approach is reasonable. I suggest you adopt this approach.

If your interval is short enough then you may use Sleep. The downside is that you need to wait for the call to Sleep to return before the thread can be terminated. So this may be a problem for longer intervals.

If you want to be able to terminate instantly you can instead of using Sleep, wait on an event with a timeout, using WaitForSingleObject, or one of the event classes defined in the SyncObjs unit. If the timeout elapses, your timer has elapsed. If the event is signaled, then your thread is expected to terminate.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top