Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Self Suspending a thread in Delphi when it's not needed and safely resuming

This question involves Delphi and XE specifically deprecating Suspend and Resume. I have read other posts and I have not found a similar usage so far, so I’m going to go ahead and ask for a discussion.

What I’d like to know is there a better way to pause a thread when it is not needed?

We have a Delphi class that we have used for years that is basically a FIFO Queue that is associated with a threaded process. The queue accepts a data object on the main thread and if the thread is suspended it will resume it.

As part of the thread’s Execute process the object is popped out of the queue and processed on the thread. Usually this is to do a database lookup.

At the end of the process a property of the object is updated and marked as available to the main thread or passed on to another queue. The last (well it really is the first) step of the Execute process is to check if there are any more items in the queue. If there is it continues, otherwise it suspends itself.

They key is the only suspend action is inside the Execute loop when it is completed, and the only resume during normal operations is called when a new item is placed in the queue. The exception is when the queue class is being terminated.

The resume function looks something like this.

process TthrdQueue.MyResume();
  begin
    if Suspended then begin
      Sleep(1); //Allow thread to suspend if it is in the process of suspending
      Resume();
    end;
  end;

The execute looks similar to this

process TthrdQueue.Execute();
  var
    Obj : TMyObject;
  begin
    inherited;
    FreeOnTerminate := true;
    while not terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        Suspend();  // No more Work
    end;   //Queue clean up in Destructor
  end;  

The TthrdQueue Push routine calls MyResume after adding another object in the stack. MyResume only calls Resume if the thread is suspended.

When shutting down we set terminate to true and call MyResume if it is suspended.

like image 976
Rich Shealer Avatar asked Dec 09 '10 17:12

Rich Shealer


People also ask

What happens when a thread is suspended?

If you suspend a thread while it holds locks during a security permission evaluation, other threads in the AppDomain might be blocked. If you suspend a thread while it is executing a class constructor, other threads in the AppDomain that attempt to use that class are blocked. Deadlocks can occur very easily.

What does it mean to suspend a thread?

The suspend() method of thread class puts the thread from running to waiting state. This method is used if you want to stop the thread execution and start it again when a certain event occurs. This method allows a thread to temporarily cease execution. The suspended thread can be resumed using the resume() method.

How do I suspend a resume thread in Linux?

Signal SIGUSR1 suspends the thread by calling pause() and SIGUSR2 resumes the thread. From the man page of pause: This example is problematic for numerous reasons. It is not thread safe: you do not wait until thread1 and thread2 set their signal handlers before calling pthread_kill.

How do I suspend main thread?

Methods Used: sleep(time): This is a method used to sleep the thread for some milliseconds time. suspend(): This is a method used to suspend the thread. The thread will remain suspended and won't perform its tasks until it is resumed.


2 Answers

I'd recommend the following implementation of TthrdQueue:

type
  TthrdQueue = class(TThread)
  private
    FEvent: THandle;
  protected
    procedure Execute; override;
  public
    procedure MyResume;
  end;

implementation

procedure TthrdQueue.MyResume;
begin
  SetEvent(FEvent);
end;

procedure TthrdQueue.Execute;
begin
  FEvent:= CreateEvent(nil,
                       False,    // auto reset
                       False,    // initial state = not signaled
                       nil);
  FreeOnTerminate := true;
  try
    while not Terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        WaitForSingleObject(FEvent, INFINITE);  // No more Work
    end;
  finally
    CloseHandle(FEvent);
  end;
end;
like image 190
kludg Avatar answered Nov 10 '22 23:11

kludg


Instead of suspending the thread, make it sleep. Make it block on some waitable handle, and when the handle becomes signalled, the thread will wake up.

You have many options for waitable objects, including events, mutex objects, semaphores, message queues, pipes.

Suppose you choose to use an event. Make it an auto-reset event. When the queue is empty, call the event's WaitFor method. When something else populates the queue or wants to quit, have it call the event's SetEvent method.

I preferred technique is to use the OS message queue. I'd replace your queue object with messages. Then, write a standard GetMessage loop. When the queue is empty, it will automatically block to wait for a new message. Turn a termination request into just another message. (The TThread.Terminate method simply isn't a very useful function once you start doing anything interesting with threads because it's not virtual.)

like image 42
Rob Kennedy Avatar answered Nov 10 '22 23:11

Rob Kennedy