Recent

Author Topic: Threads, Memos and synchronising between them  (Read 2761 times)

hdrz

  • New Member
  • *
  • Posts: 15
Threads, Memos and synchronising between them
« on: February 28, 2020, 08:37:39 am »
Hi all,

I have some old code that I need to revise and upgrade. The code essentially starts some processes using TThreads. Each thread has an associated TMemo (shown or hidden, but always present) which it fills with text. In order to proceed, I have a few questions about how to design such a thing. (The old code is working, but is not thread safe and is very hairy..)
  • Currently the main worker class is inherited from TThread with all the methods and properties under this object. I did some searching and found that usually the worker class is inherited from Object, with TThread as a private field, see for example https://www.freepascal.org/~michael/articles/lazthread/lazthread.pdf. Which is the right way?
  • How would you handle the synchronisation between the different threads and the associated TMemos? What is the correct way to store the TMemos pointers and access them from the worker objects?
I appreciate any info on the subject. Thanks all.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5446
  • Compiler Developer
Re: Threads, Memos and synchronising between them
« Reply #1 on: February 28, 2020, 09:03:57 am »
Currently the main worker class is inherited from TThread with all the methods and properties under this object. I did some searching and found that usually the worker class is inherited from Object, with TThread as a private field, see for example https://www.freepascal.org/~michael/articles/lazthread/lazthread.pdf. Which is the right way?

There really is no right no wrong. In the end it depends on how your object structure works best. In most cases I've seen it's TThread that's extended.

How would you handle the synchronisation between the different threads and the associated TMemos? What is the correct way to store the TMemos pointers and access them from the worker objects?

GUI objects must only be accessed by the main thread (some widgetsets like Qt explicitly check that). Thus any code that accesses a GUI object should do so through TThread.Synchronize, TThread.Queue or Application.QueueAsyncCall with all implications these bring with them.

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Threads, Memos and synchronising between them
« Reply #2 on: February 28, 2020, 12:46:53 pm »
How would you handle the synchronisation between the different threads and the associated TMemos?
TMemoChannel from MultiLog might not be exactly what you want, but it might be worth a look since it allows dozens of threads to write to memo without any locking and slowdown. I have used QueueAsyncCall for that, which is very nice and powerful. The only drawback I see is luck of Delphi compatibility.
https://github.com/blikblum/multilog/blob/master/memochannel.pas

Here is a wiki article on using QueueAsyncCall:
https://wiki.freepascal.org/Asynchronous_Calls
« Last Edit: February 28, 2020, 12:51:15 pm by avra »
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

hdrz

  • New Member
  • *
  • Posts: 15
Re: Threads, Memos and synchronising between them
« Reply #3 on: February 28, 2020, 01:11:28 pm »
@PascalDragon - Thank you, but as stated in the beginning of the question there is already an application I want to upgrade, so I know all the basics. I am looking for a high level point of view on the subject, as to how to manage the different memos and threads, for example how a certain thread knows to which memo to write (or maybe it doesn't know and the main application is doing the routing?), that sort of things.

@avra - Funny, I found your application somewhere else on the forum and was already looking at the code! However, in my case there may be several threads, each writing to it's own memo.

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Threads, Memos and synchronising between them
« Reply #4 on: February 28, 2020, 01:20:38 pm »
@avra

Just had a look at the memochannel.

One question: With the use of QueueAsyncCall you get around the synchronize of threads?

Winni

devEric69

  • Hero Member
  • *****
  • Posts: 648
Re: Threads, Memos and synchronising between them
« Reply #5 on: February 28, 2020, 01:27:49 pm »
For information, if you don't use SQL (the logging of when\what\where SQL queries are not yet stabilized), the fork https://github.com/devEric69/multilog/blob/master/README.md does the same thing, but is easier to use quickly (amo).
==> At the very least, the few explanations and screenshots will allow you to understand faster the concept of Multilog.
use: Linux 64 bits (Ubuntu 20.04 LTS).
Lazarus version: 2.0.4 (svn revision: 62502M) compiled with fpc 3.0.4 - fpDebug \ Dwarf3.

avra

  • Hero Member
  • *****
  • Posts: 2514
    • Additional info
Re: Threads, Memos and synchronising between them
« Reply #6 on: February 28, 2020, 02:09:44 pm »
One question: With the use of QueueAsyncCall you get around the synchronize of threads?
Yes. QueueAsyncCall simply puts in a serialized thread safe way 2 pointers (1 for method and 1 for data) into a queue. Then just before time comes for GUI update (either by OS or forced with Application.ProcessMessages) your main application thread stops execution of your code and calls all methods from the queue. It is simple as that with thread locking and syncing completely avoided. Only one thread has GUI access, so no problems at all with GUI updating from other threads.
ct2laz - Conversion between Lazarus and CodeTyphon
bithelpers - Bit manipulation for standard types
pasettimino - Siemens S7 PLC lib

winni

  • Hero Member
  • *****
  • Posts: 3197
Re: Threads, Memos and synchronising between them
« Reply #7 on: February 28, 2020, 02:45:51 pm »
Good News!

Thanx

Winni

hdrz

  • New Member
  • *
  • Posts: 15
Re: Threads, Memos and synchronising between them
« Reply #8 on: April 07, 2020, 09:31:18 pm »
OK, back to planning. Since the application I am upgrading has 1-1 relationship between thread and memo, here is my plan:
  • Make an object (class) that holds as private members descendants of TThread, TProcess, and a link to TMemo.
  • On class .Create constructor, run the process, and start the thread to monitor the process
  • On the thread.Execute procedure, let the thread push the process output strings to the memo.
  • Do I need to even use Synchronize/Queue for this? since the memo is read only, maybe the thread can add lines to memo.Lines directly?

Am I in the right direction?

Cyrax

  • Hero Member
  • *****
  • Posts: 836
Re: Threads, Memos and synchronising between them
« Reply #9 on: April 07, 2020, 10:22:09 pm »
OK, back to planning. Since the application I am upgrading has 1-1 relationship between thread and memo, here is my plan:
  • Make an object (class) that holds as private members descendants of TThread, TProcess, and a link to TMemo.
  • On class .Create constructor, run the process, and start the thread to monitor the process
  • On the thread.Execute procedure, let the thread push the process output strings to the memo.
  • Do I need to even use Synchronize/Queue for this? since the memo is read only, maybe the thread can add lines to memo.Lines directly?

Am I in the right direction?

Leave TMemo component out of the TThread class and do Queue with preallocated buffer (probably a list (stack? push and pop?) of preallocated buffers which is then freed when not used) which contains output from the process and do processing in the queue subroutine (mainly detecting line endings and adding full length strings to TMemo component etc.).

mangakissa

  • Hero Member
  • *****
  • Posts: 1131
Re: Threads, Memos and synchronising between them
« Reply #10 on: April 08, 2020, 09:23:39 am »
Quote
Leave TMemo component out of the TThread class and do Queue with preallocated buffer (probably a list (stack? push and pop?) of preallocated buffers which is then freed when not used) which contains output from the process and do processing in the queue subroutine (mainly detecting line endings and adding full length strings to TMemo component etc.).
He's right.
Creating a new event that's tells which memo the text has to be sent to.
Code: Pascal  [Select][+][-]
  1. main unit
  2. --------------------
  3. type
  4.  
  5.   TChannelProgressEvent = procedure(const ID: Integer;
  6.     const Status: String) of object;
  7.  
  8. procedure TPrintServerfrm.OnThreadProgress(const ID: Integer;
  9.   const Status: String);
  10. begin
  11.   TLabel(FLabels[ID]).Caption := Format('Kanaal %d: %s', [ID + 1, Status]);
  12. end;
  13.  
  14. thread
  15. ---------------------
  16. procedure TChannelThread.DoProgress;
  17. begin
  18.   if Assigned(FOnProgress) then
  19.     FOnProgress(ID, FProgress);
  20. end;
  21.  
  22.  
  23. Synchronize(DoProgress);
  24.  
Lazarus 2.06 (64b) / FPC 3.0.4 / Windows 10
stucked on Delphi 10.3.1

hdrz

  • New Member
  • *
  • Posts: 15
Re: Threads, Memos and synchronising between them
« Reply #11 on: April 16, 2020, 03:16:24 pm »
So after more searching I found out that my problem is the "classic producer/consumer", see for example https://forum.lazarus.freepascal.org/index.php?topic=32383.0. I guess it is classic for some...

It is also implemented in the book "Tomes of Delphi", but for windows using semaphores. Probably easy to adapt to queue/synchronize/criticalsection.

Thanks @mangakissa and @Cyrax for some more incites.

 

TinyPortal © 2005-2018