Lazarus

Programming => General => Topic started by: palacs on July 13, 2018, 09:08:31 am

Title: Best practice for a multi-platform worker thread
Post by: palacs on July 13, 2018, 09:08:31 am
I'd like to have a worker TThread which I utilize by sending messages from the main (UI) thread so it should be in a blocked state waiting for messages then if received, start some kind of processing according to the received message type. When done, it should send a message back to the main thread (UI) which will then display results. I was experimenting with PostMessage but the only part I could achieve is sending a message to the UI thread to display results.

Code: [Select]
unit threadtest;

{$mode objfpc}{$H+}

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  LMessages, LCLIntf;

const
  UM_RESULT = WM_USER + 1;

type
  TWorker = class(TThread)
    private
      textbuffer: String;
    protected
      procedure Execute; override;
  end;

  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    worker: TWorker;
  protected
    procedure UMResult(var Message: TLMessage); message UM_RESULT;
  public

  end;

var
  Form1: TForm1;

implementation

{$R *.lfm}

{ TForm1 }

procedure TForm1.Button1Click(Sender: TObject);
begin
  worker := TWorker.Create(False);
end;

procedure TForm1.UMResult(var Message: TLMessage);
begin
  Memo1.Lines.Add('Message: ' + String(Message.WParam));
end;

procedure TWorker.Execute;
begin
  // Imitate to do something
  Sleep(1000);

  // Send message to UI thread
  textbuffer := DateTimeToStr(Now);
  LCLIntf.PostMessage(Form1.Handle, UM_RESULT, PtrInt(PString(textbuffer)), 0);
end;

end.

What is the best basic cross-platform workflow to do this?

How should I write a while not Terminated ... loop into TWorker.Execute that would catch messages and call functions within TWorker? Delphi tutorials talk about GetMessage but I didn't find them anywhere in LCLIntf.
Title: Re: Best practice for a multi-platform worker thread
Post by: schuler on July 13, 2018, 09:45:12 am
@palacs.
Your question is in my interest. I've had horrible experiences in Linux in regards to this (although I generally like Linux).

Anyway, some of my difficulties have been posted here:
https://forum.lazarus.freepascal.org/index.php/topic,41477.0.html

Title: Re: Best practice for a multi-platform worker thread
Post by: palacs on July 14, 2018, 02:08:14 pm
I just can't believe that this is so hard and there is no simple pattern. Communicating with a thread should be a basic feature in GUI programming, what Lazarus is for.
Title: Re: Best practice for a multi-platform worker thread
Post by: balazsszekely on July 14, 2018, 04:16:32 pm
@palacs
Quote
I just can't believe that this is so hard and there is no simple pattern. Communicating with a thread should be a basic feature in GUI programming, what Lazarus is for.
It is a basic feature. Please test attached project. Count is just a dummy method, you should replace it with something useful. Feel free to ask more questions if you like.
Code: Pascal  [Select][+][-]
  1. unit uThread;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils;
  9.  
  10. type
  11.   TThreadStatus = (tsCount, tsIDLE);  //extend this according to your needs
  12.  
  13.   TOnBeginCount = procedure(Sender: TObject; AMsg: String) of object;
  14.   TOnCount = procedure(Sender: TObject; ATotCnt, ACur: Integer) of object;
  15.   TOnEndCount = procedure(Sender: TObject; AMsg: String) of object;
  16.  
  17.   { TWorkerThread }
  18.   TWorkerThread = class(TThread)
  19.   private
  20.     FThreadStatus: TThreadStatus;
  21.     FOnBeginCount: TOnBeginCount;
  22.     FOnCount: TOnCount;
  23.     FOnEndCount: TOnEndCount;
  24.     FTotCnt: Integer;
  25.     FCur: Integer;
  26.     procedure Count;
  27.     procedure DoBeginCount;
  28.     procedure DoCount;
  29.     procedure DoEndCount;
  30.   protected
  31.     procedure Execute; override;
  32.   public
  33.     constructor Create;
  34.     destructor Destroy; override;
  35.   public
  36.     property ThreadStatus: TThreadStatus read FThreadStatus write FThreadStatus;
  37.     property OnBeginCount: TOnBeginCount read FOnBeginCount write FOnBeginCount;
  38.     property OnCount: TOnCount read FOnCount write FOnCount;
  39.     property OnEndCount: TOnEndCount read FOnEndCount write FOnEndCount;
  40.   end;
  41.  
  42. implementation
  43.  
  44. { TWorkerThread }
  45.  
  46. procedure TWorkerThread.DoCount;
  47. begin
  48.   if Assigned(FOnCount) then
  49.     FOnCount(Self, FTotCnt, FCur);
  50. end;
  51.  
  52. procedure TWorkerThread.DoBeginCount;
  53. begin
  54.   if Assigned(FOnBeginCount) then
  55.     FOnBeginCount(Self, 'Worker thread: I''m gonna count to 100.');
  56. end;
  57.  
  58. procedure TWorkerThread.Count;
  59. var
  60.   I: Integer;
  61. begin
  62.   Synchronize(@DoBeginCount);
  63.   try
  64.     //replace this part with something useful
  65.     FTotCnt := 100;
  66.     for I := 0 to FTotCnt do
  67.     begin
  68.       FCur := I;
  69.       Synchronize(@DoCount);
  70.       Sleep(50);
  71.     end;
  72.   finally
  73.     FThreadStatus := tsIDLE;
  74.     Synchronize(@DoEndCount);
  75.   end;
  76. end;
  77.  
  78. procedure TWorkerThread.DoEndCount;
  79. begin
  80.   if Assigned(FOnEndCount) then
  81.     FOnEndCount(Self, 'Worker thread: End count, status is IDLE.');
  82. end;
  83.  
  84. procedure TWorkerThread.Execute;
  85. begin
  86.   while not Terminated do
  87.   begin
  88.     case FThreadStatus of
  89.       tsCount: Count;
  90.       tsIDLE: Sleep(50);
  91.     end;
  92.   end;
  93. end;
  94.  
  95. constructor TWorkerThread.Create;
  96. begin
  97.   inherited Create(True);
  98.   FreeOnTerminate := True;
  99.   FThreadStatus := tsIDLE;
  100. end;
  101.  
  102. destructor TWorkerThread.Destroy;
  103. begin
  104.   inherited Destroy;
  105. end;
  106.  
  107. end.
Title: Re: Best practice for a multi-platform worker thread
Post by: BeniBela on July 14, 2018, 05:39:10 pm
Synchronize only works with a main thread, does it not?

I never could get it to work on Android
Title: Re: Best practice for a multi-platform worker thread
Post by: Thaddy on July 14, 2018, 06:29:17 pm
Synchronize only works with a main thread, does it not?

I never could get it to work on Android
Why? synchronize is between threads, not only the main thread. Or it should be...
Title: Re: Best practice for a multi-platform worker thread
Post by: balazsszekely on July 14, 2018, 06:45:48 pm
@BeniBela
Quote
Synchronize only works with a main thread, does it not?
Yes.

@Thaddy
Quote
Why? synchronize is between threads, not only the main thread.
No. Is between a worker and the main thread. The AMethod specified in the parameter is called in the context of the main thread. Synchronize is blocking, the thread is waiting until the code is executed in the main thread. For asynchronous processing you can use Queue.

PS: Synchronize /Queue is only needed when you wish to update visual controls.
Title: Re: Best practice for a multi-platform worker thread
Post by: Thaddy on July 14, 2018, 07:08:11 pm
@Thaddy
Quote
Why? synchronize is between threads, not only the main thread.
No. Is between a worker and the main thread. The AMethod specified in the parameter is called in the context of the main thread. Synchronize is blocking, the thread is waiting until the code is executed in the main thread. For asynchronous processing you can use Queue.

PS: Synchronize /Queue is only needed when you wish to update visual controls.
Not it is NOT. Basics. It is about the initiating thread - that happens to be often the main thread...... This is silly..If you missed that?  back to school <grumpyish  :D>
I have trees of threads running...
Title: Re: Best practice for a multi-platform worker thread
Post by: lainz on July 14, 2018, 07:29:05 pm
In delphi is like this
http://docwiki.embarcadero.com/Libraries/Tokyo/en/System.Classes.TThread.Synchronize
Title: Re: Best practice for a multi-platform worker thread
Post by: balazsszekely on July 14, 2018, 07:30:24 pm
@Thaddy
Quote
Not it is NOT. Basics. It is about the initiating thread - that happens to be often the main thread...... This is silly..If you missed that?  back to school <grumpyish  :D>
I have trees of threads running...
Too much talk is a waste of time, at least this is how I see it. Luckily everyone who is interested in the subject can do a quick google search.
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 14, 2018, 11:23:02 pm
https://forum.lazarus.freepascal.org/index.php/topic,41802.0.html (https://forum.lazarus.freepascal.org/index.php/topic,41802.0.html)

do not use messages outside main LCL thread because they are not crossplatform.

use 'Post a Method' and the Done boolean variable to test, or the Callback I designed to communicate
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 14, 2018, 11:43:47 pm
you just need to learn how to use 'array of const', then use my TPowerThread or TThreadedComponent from version 0.5
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 14, 2018, 11:46:22 pm
https://www.freepascal.org/docs-html/ref/refsu69.html#x181-20300014.4.6 (https://www.freepascal.org/docs-html/ref/refsu69.html#x181-20300014.4.6)
Title: Re: Best practice for a multi-platform worker thread
Post by: wadman on July 16, 2018, 08:30:01 am
In my solution, an additional thread is used to emulate the message queue.



You can see here: https://github.com/wadman/wthread
Unfortunately, some of the comments are in russian.
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 16, 2018, 02:19:04 pm
In my solution, an additional thread is used to emulate the message queue.



You can see here: https://github.com/wadman/wthread
Unfortunately, some of the comments are in russian.

AS I mentioned here, WMessages are NOT crossplatform. As I wrote elsewhere, your WThread package is fine on windows/Delphi but have some issues on other platforms. That is even the reason which gave me the need to write my own TPowerThread.
https://forum.lazarus.freepascal.org/index.php/topic,41802.0.html (https://forum.lazarus.freepascal.org/index.php/topic,41802.0.html)
1) your WThread uses WMessage (not adviced on other platforms but windows)
2) you WThread loves variants, they are SLOW and disturb the gain around multithreading
3) why use a complicated solution when a simplier one can be efficient?
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 16, 2018, 02:24:24 pm
It's hard to be simple and to keep it easy hehehehe. Only Masters can!
 >:D
Title: Re: Best practice for a multi-platform worker thread
Post by: wadman on July 16, 2018, 02:53:49 pm
1) your WThread uses WMessage (not adviced on other platforms but windows)
But it's magically working on other operating systems. :)


Use variants or not - the programmer decides. The source code is open.
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 16, 2018, 05:13:32 pm
mine is closed?
Title: Re: Best practice for a multi-platform worker thread
Post by: Thaddy on July 16, 2018, 06:00:36 pm
How about the examples here: https://www.freepascal.org/docs-html/rtl/classes/tthread.executeinthread.html
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 16, 2018, 09:06:51 pm
How about the examples here: https://www.freepascal.org/docs-html/rtl/classes/tthread.executeinthread.html
ExecuteInThread fires a single method in a created for this purpose thread and then stops that thread. That is far different from having a thread running a loop awaiting multiple orderS from main or other threads
Title: Re: Best practice for a multi-platform worker thread
Post by: Thaddy on July 16, 2018, 09:11:08 pm
How about the examples here: https://www.freepascal.org/docs-html/rtl/classes/tthread.executeinthread.html
ExecuteInThread fires a single method in a created for this purpose thread and then stops that thread. That is far different from having a thread running a loop awaiting multiple orderS from main or other threads
WHAT is different? You can implement such a loop inside the procedure...
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 16, 2018, 09:16:56 pm

WHAT...
Stop barking at the moonlight, grab a book and learn instead of telling others to do so :P
when I said FAR different , think about re-entrance, and reusable variables
ExecuteInThread is not reentrant
Derived Threads looping can be, with local variables inside the derived designed thread
Title: Re: Best practice for a multi-platform worker thread
Post by: Thaddy on July 16, 2018, 10:02:35 pm
Assignable typed constants are re-entrant..... No problems there...
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 17, 2018, 03:53:06 am
Assignable typed constants are re-entrant..... No problems there...
cool for you in your tiny amateur world  :P can you please explain how you run a 'tree of threads' with yer poor knowledge Ima curious bird  :D :D :D
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 17, 2018, 03:53:56 am

Not it is NOT. Basics. It is about the initiating thread - that happens to be often the main thread...... This is silly..If you missed that?  back to school <grumpyish  :D>
I have trees of threads running...

bbbHAAHHAHAAHAHAHAH threadpool or deadpool that is the kuestion. A tree or a scrubland? hahahahahahaha
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 17, 2018, 05:36:14 am
https://forum.lazarus.freepascal.org/index.php?topic=41802.msg291843#msg291843 (https://forum.lazarus.freepascal.org/index.php?topic=41802.msg291843#msg291843)
Title: Re: Best practice for a multi-platform worker thread
Post by: balazsszekely on July 17, 2018, 06:18:15 am
@mercurhyo
One Thaddy/forum is more then enough, no need to play with the same cards as him. More over it seems like the following users are the same: sam707, pigrimm, gulyone, mercurhyo.  Same jokes, same posting style like the uppercase "HAAHHAHAAHAHAHAH". More over neither one of you edit the original post, you're inclined to post over and over again. I hope you don't suffer from multiple personality disorder.
Title: Re: Best practice for a multi-platform worker thread
Post by: mercurhyo on July 17, 2018, 06:59:35 am
I also am Thaddy, queen Mary, Bill Cody, king Richard, Vlad Tepes, and... your mom! didn't you guess, paranoid @getmem?
 :D
Title: Re: Best practice for a multi-platform worker thread
Post by: balazsszekely on July 17, 2018, 07:13:02 am
Quote
I also am Thaddy
You're definitely not Thaddy, though both of you are childish.

Quote
queen Mary, Bill Cody, king Richard, Vlad Tepes, and... your mom!
Lol just like sam707.

PS: I leave you with Thaddy, I have better things to do.

 
TinyPortal © 2005-2018