Jump to content
dkounal

Return results to main thread from TTask with Thread.Queue

Recommended Posts

I need to run a piece of code in TTask threads using a thread pool, that runs in every platform and get the results in the main UI thread in a thread safe message object without checking with a timer.
Inspired by a post from Remy Lebeau in the Lazarus forum I wrote the following.
Comments, suggestions, errors in the code welcome.
 
interface

type
TTaskdata < T >= class
  type mydataP = ^T;
  mytaskproctyp =procedure(a: mydataP) of object;
private
  Data: T;
  ProcessData: mytaskproctyp;
  procedure DoProcess;
public
  procedure queue(datatyp: T; taskproc: mytaskproctyp);
  end;

implementation

procedure TTaskdata<T>.queue(datatyp: T; taskproc: mytaskproctyp);
begin
  Data := datatyp;
  ProcessData := taskproc;
  TThread.queue(nil, DoProcess);
end;

procedure TTaskdata<T>.DoProcess;
begin
  ProcessData(@Data);
end;

In an other/main Tform Unit
type
  Dsoup = record
    nam: string;
    i: integer;
  end; // a sample record for send and receiving data to Ttask routine

  ReturnProc = procedure(mypointer: Ttaskdata<Dsoup>.mydataP) of object;

var
  FCustomPool: TThreadPool;
  // the theadpool to be used for reusing threads, it should initialiazed in Tform's constructor

procedure TForm1.Button1Click(Sender: TObject);
// the initial route that will prepare data for Ttask and run it
var
  soup: Dsoup;
begin
  { set in Dsoup all data to send in TTask routine }
  TTask.Run(dowork(soup, getresults), FCustomPool);
end;

function TForm1.dowork(const a: Dsoup; p: ReturnProc): tproc;
begin
  Result := // anonymous function to run by Ttask
      procedure
    var
      ff: Ttaskdata<Dsoup>;
    begin
      ff := Ttaskdata<Dsoup>.create;
      try
        { do all the task job here }
        ff.queue(a, p); // add result data to be sent with Thread.queue
      except
        ff.free;
      end;
    end;
end;

procedure TForm1.getresults(p: Ttaskdata<Dsoup>.mydataP);
begin
  { here come the results from TTask in the main UI thread }
end;

 

 
Edited by dkounal
better presentation of code

Share this post


Link to post
53 minutes ago, dkounal said:

Comments, suggestions, errors in the code welcome.

Call me old fashioned but the way you've formatted your code makes it close to unreadable to me.

  • Like 1

Share this post


Link to post
10 minutes ago, Anders Melander said:

Call me old fashioned but the way you've formatted your code makes it close to unreadable to me.

Is it better now?

Share this post


Link to post

Borland / CodeGear / Embarcadero / Jedi coding standard.
You can see for example how the formatting of System.Classes

Share this post


Link to post
1 hour ago, Marat1961 said:

Borland / CodeGear / Embarcadero / Jedi coding standard.
You can see for example how the formatting of System.Classes

I apologize. Formated using Delphi's IDE "format source"  procedure

Share this post


Link to post

You just need to configure the auto format according to the formatting standard.

This format is usually the default.

 

If you think a little, then the following conclusion follows from here:
Delphi has only one correct coding standard, and all the others are wrong.

 

When you invite other people to see the code,

don't surprise them with your absolutely amazing and most perfect style of code formatting.

Edited by Marat1961

Share this post


Link to post
7 minutes ago, Marat1961 said:

You just need to configure the auto format according to the formatting standard.

This format is usually the default.

 

If you think a little, then the following conclusion follows from here:
Delphi has only one correct coding standard, and all the others are wrong.

 

Delphi IDE 10.4.1 -> Tools -> Options -> Language ->Formatter ->select profile "Formater Default config" -> Apply.

The code remains the same after Right Click->Format Source

Am I missing something? Thank you in advance for your help

Share this post


Link to post
18 hours ago, dkounal said:

type

  TTaskdata<T>= class

    type mydataP = ^T;

    mytaskproctyp = procedure(a: mydataP) of object;

This code was definitely not passed through the autoformat.
You still don't know what the Soviet norm - control is! :classic_biggrin:

Share this post


Link to post

It was first done well in this book,
Hoare, C. A. R. Communicating sequential processes

 

I have a book "Patterns in Java. A Catalog of Reusable Design Patterns Illustrated with UML | Grand Mark".

 

This book describes the Producer-Consumer pattern, in my opinion this is your case.
There are code examples for the queue.

 

  • Like 1

Share this post


Link to post
13 minutes ago, Marat1961 said:

This code was definitely not passed through the autoformat.
You still don't know what the Soviet norm - control is! :classic_biggrin:

You are correct. This was my only modification as it was reformatted completly unreadable. I give up. Please tell me what to do

Share this post


Link to post

I would use the MCV pattern.


The user interface must be subscribed to change the data model.


Receiver and consumer must be processes.
When the model changes, notify the user interface about the need to update the presentation.

Supplier => Consumer => Model

MainForm.Refresh;

 

The timer, by the way, is useful in case of a group of changes.
So that there is no fuss under the client, it is useful not to draw everything at once, but with some delay of at least 0.2 - 0.5 seconds.

procedure TMainForm.RefreshTimerTimer(Sender: TObject);
begin
  RefreshTimer.Enabled: = False;
  Refresh;
end;

procedure TMainForm.ModelChanged(Sender: TObject);
begin
  RefreshTimer.Rearm;
end;

 

Edited by Marat1961

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×