2

I have a db application that fetches db record-set asynchronously very similatr to Threaded Delphi ADO Query

Each time the user clicks refresh a new TDBThread is created to get a record-set.

When that happens I want to discard all previous requests and process only the last one.

So I made a class field called FRequestID which I increment each time a request is made. I do not attempt to cancel/abort previous requests and I don't keep reference to the threads created.

procedure TForm1.RefreshClick(Sender: TObject);
var
  T: TDBThread;
begin
  Inc(FRequestID);

  T := TDBThread.Create(True); // Suspended
  T.FreeOnTerminate := True;
  T.RequestID := FRequestID;
  T.SQL := 'select * from mytable where ...';  
  T.OnTerminate := DBThreadTerminate;
  T.Resume;
end;

And on terminate I check if the the thread RequestID is the last FRequestID, and only then I handle the request.

procedure TForm1.DBThreadTerminate(Sender: TObject);
var
  T: TDBThread;
begin
  T := TDBThread(Sender);
  if FRequestID = T.RequestID then
  begin  
    Memo1.Lines.Add('*** Thread terminated ok ' + IntToStr(T.RequestID));
    MainDS.RecordSet := T.RecordSet;
  end
  else
    Memo1.Lines.Add('Thread discarded: ' + IntToStr(T.RequestID ));
end;

My question is this approach correct (and thread safe) and if there is a better way to handle only the last request?

Note: I'm on Delphi 7.

zig
  • 4,524
  • 1
  • 24
  • 68
  • 2
    I would approach that differently. Disable the refresh button when the Thread is doing its work. Then there is no need for multiple threads, no need for any integer that might Not be accessed thread-safe. Maybe link an TAction to the button, use the OnUpdate event to disable it if DBThread is running. – nil Sep 09 '17 at 12:38
  • 2
    @Nil, Thanks, It does not matter if it's a button click or a `TAction` attached. You can not deny the user from hitting the refresh or performing a new query (with new search conditions e.g) while the DBThread is running. at-least not in my program. – zig Sep 09 '17 at 12:47
  • I think you'll need to implement some of this https://stackoverflow.com/questions/4044855/how-to-kill-a-thread-in-delphi – John Easley Sep 09 '17 at 21:48

1 Answers1

0

Your code is thread safe and correct in the meaning of what you defined (I'm not counting with that you overflow integer limits for that request ID). It seems that you're aware that you won't be able to terminate thread that you have no reference to and that you're wasting resources. It's worth adding that you wouldn't be able to cancel running DBMS operation as the code you've linked implements synchronous fetch, just executed inside a worker thread and ADO cannot cancel these.

There is a better way to do this though. You can e.g. create only one thread (because creating one thread per task is inefficient for frequent tasks), have a collection of commands (queue or list), and let the thread sleep awaiting for a work signalled by a system event.

Or instead of command collection one such thread per command (but here you should think about number of commands used in your application).

And you can implement asynchronous ADO execution (which can be cancelled, so you'd interrupt running operation and execute another).

Victoria
  • 7,822
  • 2
  • 21
  • 44