Recent

Author Topic: Moving from TThread.Synchronize to TThread.Queue  (Read 31240 times)

cpicanco

  • Hero Member
  • *****
  • Posts: 674
  • Behavioral Scientist and Programmer
    • Portfolio
Moving from TThread.Synchronize to TThread.Queue
« on: September 15, 2017, 02:01:30 am »
TThread.Synchronize is blocking, TThread.Queue is not (it returns immediately).

Ok.

If you need to send some data to the main thread, you can do something like:

Code: Pascal  [Select][+][-]
  1. // TThread class
  2. private
  3.   FData : TStringList;
  4.  
  5. procedure SendData;
  6. begin
  7.   // do something with a copy of FData
  8. end;
  9.  
  10. // execute method
  11. begin
  12.   while not Terminated do
  13.   begin
  14.     FData := ReceiveData; // ReceiveData will block until some data is received
  15.     Synchronize(@SendData); // will block until finnished, so you may miss some data
  16.     // Queue(@SendData); // would return immediatelly, promptly to receive more data
  17.   end;
  18. end;
  19.  

Assuming that Queue would optimize data receivement, a problem you may face is that you must copy FData somehow to avoid overrides.

QUESTION

How to make such a copy???
Be mindful and excellent with each other.
https://github.com/cpicanco/

sam707

  • Guest
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #1 on: September 15, 2017, 02:39:59 am »
in both case (synchronise and queue)
ReceiveData is executed in your thread while SendData is Executed in main thread

If Queue used ===>
I suggest you to suspend your thread in your SendData method, to ensure that the data is not changed by an inthread concurrent ReceiveData. results are unpredicable, even crashes! (sending data while they are modified = BANG)

Either suspend, or use critical sections... see TCriticalSection for an easy way, TRTLCriticalSection record for a manually Lock control

procedure SendData;
begin
  suspended:=True;
  // do something directly with  FData
  suspended:=False;
end;
« Last Edit: September 15, 2017, 02:54:20 am by sam707 »

sam707

  • Guest
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #2 on: September 15, 2017, 02:59:38 am »
anyway, in such case, synchronize is better, ensuring the thread do not read new arriving data, before they have been sent

the "suspended:=True" is a plaeholder trick that is not meant to always work if your CPU is very very fast and data has changed before you suspended the thread.
« Last Edit: September 15, 2017, 05:09:27 am by sam707 »

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #3 on: September 15, 2017, 03:16:11 am »
Perhaps i'm daft, but can't you allocate the buffer in receive() and free again in send() so that you don't have to use the exact same buffer ?

Your initial approach to copy the buffer would take the same amount of time anyway (minus the time spend on copying the buffer) so better do it at a more appropriate location ?

« Last Edit: September 15, 2017, 03:21:04 am by molly »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 882
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #4 on: September 15, 2017, 07:46:08 am »
How to make such a copy???
Synchronize kills all the purpose of having thread: you don't do two things at the same time - you do them one after another.

There is solution, that has been known since first computers, where this solution was used to, for example, reading keys from keyboard. Solution is simple - use buffer.
Code: Pascal  [Select][+][-]
  1. // TThread class
  2. private
  3.   FBuffer:TThreadededQueue<TStringList>;//Simple example - it's not that hard to construct such container by yourself
  4.  
  5. procedure TMyThread.Execute;
  6. begin
  7.   while not Terminated do
  8.   begin
  9.     //Warning! If main thread hangs or can't process data fast enough - buffer may start inflating and eating your memory.
  10.     //You should monitor this situation! Buffer size should be limited for example.
  11.     FBuffer.PushItem(ReceiveData);
  12.   end;
  13. end;
  14.  
  15. function TMyThread.GetData:TStringList;
  16. begin
  17.   Result := FBuffer.PopItem;//Don't forget to set PopTimeout!!!
  18. end;
  19.  
  20. //Main thread
  21.   var Data:TStringList;
  22. begin
  23.   ...
  24.   Data := MyThread.GetData;
  25.   if Assigned(Data) then...
  26.   ...
  27. end;
  28.  
« Last Edit: September 15, 2017, 08:01:56 am by Mr.Madguy »
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #5 on: September 15, 2017, 08:04:11 am »
How to make such a copy???
There is solution, that has been known since first computers, where this solution was used to, for example, reading keys from keyboard. Solution is simple - use buffer.
Strange... i don't see data being copied anywhere.  :)

What i do see is code that would enable for a bunch of stringlists to be pushed back 'n forward. So, basically using multiple buffers instead of just one single one (sounds familiar ? ;-P )

The longer i look at the original question the more i am convinced TS is actually looking for a queued threadsafe stringlist. But then i don't understand the original goal as running multiple threads on the same stringlist would mean that data is stored in random order. And that doesn't sound right for receiving and sending data as that is usually done in a fairly orderly fashion.

sam707

  • Guest
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #6 on: September 15, 2017, 08:06:13 am »

There is solution, that has been known since first computers, where this solution was used to, for example, reading keys from keyboard. Solution is simple - use buffer.


huh threads and sync well known since 1st computers? Okay FIFO containers (First in First Out rolling buffers, yes), but synchronizing while entries can arrive faster than computed outgoing result, NOWAY

I don't want a trial from what you smoke lately LOL when you comment

FBuffer:TThreadededQueue<TStringList>;//Simple example - it's not that hard to construct such container by yourself

HAHAHAH

sam707

  • Guest
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #7 on: September 15, 2017, 08:07:26 am »
yup molly +10, Mr.Madguy -1, so far

sam707

  • Guest
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #8 on: September 15, 2017, 08:11:35 am »
easy to write general generic container???? COME ON! go for it and let us laugh

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 882
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #9 on: September 15, 2017, 08:12:29 am »
I just thought, that TStringList - is data, not contents of TStringList, cuz ReceiveData returns exactly TStringList - not adds data to existing TStringList. Just change it to this then:
Code: Pascal  [Select][+][-]
  1. // TThread class
  2. private
  3.   FBuffer:TThreadededQueue<String>;//Simple example - it's not that hard to construct such container by yourself
  4.  
  5. procedure TMyThread.Execute;
  6. begin
  7.   while not Terminated do
  8.   begin
  9.     //Warning! If main thread hangs or can't process data fast enough - buffer may start inflating and eating your memory.
  10.     //You should monitor this situation! Buffer size should be limited for example.
  11.     FBuffer.PushItem(ReceiveData);
  12.   end;
  13. end;
  14.  
  15. function TMyThread.GetData:String;
  16. begin
  17.   Result := FBuffer.PopItem;//Don't forget to set PopTimeout!!!
  18. end;
  19.  
  20. //Main thread
  21.   var Data:String;
  22. begin
  23.   ...
  24.   Data := MyThread.GetData;
  25.   if Data <> '' then...
  26.   ...
  27. end;
  28.  
It's not that hard to construct TThreadedQueue by yourself. You just need TQueue, that is accessed inside critical section only (or Mutex if you need to pass data to another process).
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

molly

  • Hero Member
  • *****
  • Posts: 2330
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #10 on: September 15, 2017, 08:18:05 am »
@Mr.Madguy:
I approached it using your first posted solution, but as part of the thread itself.

Although it can be done on a global queue as well but that just didn't made any sense to me when running a multiple of TS' threads.

But then again, and as mentioned by sam707: it is an accident waiting to happen if you don't limit things.

I'll trade this thread crap for a well written interrupt handler any day   :)
« Last Edit: September 15, 2017, 08:23:56 am by molly »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 882
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #11 on: September 15, 2017, 10:38:05 am »
huh threads and sync well known since 1st computers? Okay FIFO containers (First in First Out rolling buffers, yes), but synchronizing while entries can arrive faster than computed outgoing result, NOWAY

I don't want a trial from what you smoke lately LOL when you comment

FBuffer:TThreadededQueue<TStringList>;//Simple example - it's not that hard to construct such container by yourself

HAHAHAH
Yeah, what TS needs - is actually interrupt handler. Something similar to keyboard interrupt handler. You know, yeah, interrupt handlers were first way to implement hardware multitasking and multithreading. Actually, even on moderns OSs when you have only one core - threads are being switched in timer interrupt handler. What keyboard interrupt was? It was "Interrupt current process, read key from keyboard and then store it to buffer, so keys won't be lost, if main thread won't be able to read them before next keyboard event would occur". TS needs exactly the same.

And there is simple way to emulate interrupt - threads. Thread waits for event from hardware, than puts data to queue, so data won't be lost, if next hardware event will occur before main thread would be able to handle previous one, and then main thread pulls data from this queue. Queue - is the only global object in this case and it is blocked only during reading/writing - not during whole data processing in main thread. Two things are being done at the same time - maximum thread effectivity. Cuz thread is useless, if you block it to do something in main thread - you shouldn't even use thread in this case.

What thread should do:
Code: Pascal  [Select][+][-]
  1.   while not Terminated do begin
  2.     Data := ReadData;
  3.     EnterCriticalSection;
  4.     Queue.Push(Data);
  5.     LeaveCriticalSection;
  6.   end;
  7.  

What main thread should do to read data:
Code: Pascal  [Select][+][-]
  1.   EnterCriticalSection;
  2.   Data := Queue.Pull;
  3.   LeaveCriticalSection;
  4.  

If you don't know, how to implement queue - it's looped buffer. Yeah, you can implement stack via list, but queue would require too much data copying in this case. And of course I won't do it for you. I just used simple example, where fully featured object, made specifically for this purpose, is used. But again, it's not that hard to implement looped buffer + critical section to sync access to it by yourself.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Thaddy

  • Hero Member
  • *****
  • Posts: 19163
  • Glad to be alive.
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #12 on: September 15, 2017, 10:47:47 am »
The guards are already handled in the TThreadedQueue from generics.containers.(In trunk) But I'd rather see the use of a lock-free queue, because in TTrhreadedQueue the whole queue is locked for writing. It should be locked on a per item basis. I'll see if I can get permission to publish mine.
Benito van de Zander has also something similar - a lock-free queue- and he has already published it if I am not mistaken, but I can't find it right now. Benito?
« Last Edit: September 15, 2017, 10:52:46 am by Thaddy »
objects are fine constructs. You can even initialize them with constructors.

Remy Lebeau

  • Hero Member
  • *****
  • Posts: 1590
    • Lebeau Software
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #13 on: September 16, 2017, 12:16:28 am »
Assuming that Queue would optimize data receivement, a problem you may face is that you must copy FData somehow to avoid overrides.

QUESTION

How to make such a copy???

For example, in Indy, its TIdNotify class (which wraps TThread.Queue()) sends queued data by implementing the queued method inside an object that gets freed when the queued method exits.  You can do something similar, eg:

Code: Pascal  [Select][+][-]
  1. type
  2.   TDataProcessor = class
  3.   private
  4.     FData : TStringList;
  5.     procedure DoProcess;
  6.     procedure ProcessData;
  7.   public
  8.     constructor Create;
  9.     destructor Destroy; override;
  10.     procedure Queue;
  11.     property TStringList Data read FData;
  12.   end;
  13.  
  14. constructor TDataProcessor.Create;
  15. begin
  16.   inherited
  17.   FData := TStringList.Create;
  18. end;
  19.  
  20. destructor TDataProcessor.Destroy;
  21. begin
  22.   FData.Free;
  23.   inherited;
  24. end;
  25.  
  26. procedure TDataProcessor.DoProcess;
  27. begin
  28.   try
  29.     ProcessData;
  30.   finally
  31.     Free;
  32.   end;
  33. end;
  34.  
  35. procedure TDataProcessor.ProcessData;
  36. begin
  37.   // do something with FData
  38. end;
  39.  
  40. procedure TDataProcess.Queue;
  41. begin
  42.   TThread.Queue(nil, @DoProcess);
  43. end;
  44.  
  45. ...
  46.  
  47. procedure MyThread.Execute;
  48. var
  49.   Processor: TDataProcessor;
  50. begin
  51.   while not Terminated do
  52.   begin
  53.     Processor := TDataProcessor.Create;
  54.     try
  55.       ReceiveData(Processor.Data); // ReceiveData will block until some data is received
  56.       Processor.Queue;
  57.     except
  58.       Processor.Free; // not queued, free now...
  59.     end;
  60.   end;
  61. end;
  62.  
  63. procedure MyThread.ReceiveData(AData: TStringList);
  64. begin
  65.   AData.Add(...);
  66. end;
  67.  
« Last Edit: September 16, 2017, 01:06:29 am by Remy Lebeau »
Remy Lebeau
Lebeau Software - Owner, Developer
Internet Direct (Indy) - Admin, Developer (Support forum)

taazz

  • Hero Member
  • *****
  • Posts: 5368
Re: Moving from TThread.Synchronize to TThread.Queue
« Reply #14 on: September 16, 2017, 12:41:49 am »
and that works? The code you posted looks to me that it will free the processor before it has finished processing, only by chance it might free it after it finished.
What am I missing? Did you mend to use synchronise?
Good judgement is the result of experience … Experience is the result of bad judgement.

OS : Windows 7 64 bit
Laz: Lazarus 1.4.4 FPC 2.6.4 i386-win32-win32/win64

 

TinyPortal © 2005-2018