Recent

Author Topic: How to create form in thread?  (Read 3521 times)

lazpas

  • Jr. Member
  • **
  • Posts: 83
How to create form in thread?
« on: May 05, 2023, 07:18:52 am »
Thanks.

eljo

  • Sr. Member
  • ****
  • Posts: 468
Re: How to create form in thread?
« Reply #1 on: May 05, 2023, 08:00:49 am »
you don't.

Zoran

  • Hero Member
  • *****
  • Posts: 1830
    • http://wiki.lazarus.freepascal.org/User:Zoran
Re: How to create form in thread?
« Reply #2 on: May 05, 2023, 11:46:52 am »
Apart from the main thread, no other thread is allowed to interact with GUI.
I don't remember why it is so, but I know that this limitation exists not only in LCL, but also in Delphi's VCL, as well as in other GUI libraries used with other popular programming languages.

Having said that, you can freely access LCL from another thread (let's call it Thread2) in synchronized methods of Thead2, which actually means that Thread2 stops executing and passes the method to the main thread, to be executed in the context of the main thread; only after the main thread is finished with this method, Thread2 continues its own life.

An example here: https://www.thoughtco.com/synchronizing-threads-and-gui-delphi-application-1058159
This example updates progress bars, but it is the same with creating new forms or any interaction with LCL controls.
« Last Edit: May 05, 2023, 11:48:27 am by Zoran »

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: How to create form in thread?
« Reply #3 on: May 05, 2023, 04:22:36 pm »
I don't remember why it is so, but I know that this limitation exists not only in LCL, but also in Delphi's VCL, as well as in other GUI libraries used with other popular programming languages.

Short answer, threading is hard and GUI system APIs are already complex enough with all their events and interactions. If you start having them in parallel this would be impossible to debug and test. Both the restriction of the LCL and Delphis VCL comes from the underlying system. Basically the windows API does not support threaded access to GUIs, it's the same for QT and while Cocoa does AFAIK not enforce this, it is also recommended to not access the GUI from other threads there. So it's not an LCL or Lazarus limitation, but a limitation of the underlying systems and APIs.

This is for example why javascript has introduced async-await style co routines, to allow parallelism on a single thread, because as a mostly GUI and interactive system focused language threading is very limited in javascript, but instead non preemptive co-routines are used to avoid the problems of having to synchronize threads.

It should also be noted that most datatypes in Pascal are not thread safe either, Arrays, Strings and most classes are not threadsafe.
« Last Edit: May 05, 2023, 04:24:16 pm by Warfley »

cai

  • New Member
  • *
  • Posts: 41
Re: How to create form in thread?
« Reply #4 on: May 09, 2023, 05:47:13 am »
actually,on windows, API: MessageBox can be called in a sub-thread, maybe it is specail API, or coincidence,it dose not mean MS stand with that!

lazpas

  • Jr. Member
  • **
  • Posts: 83
Re: How to create form in thread?
« Reply #5 on: May 19, 2023, 05:08:11 am »
eljo,Zoran,Warfley,cai,thanks for the reply.

@Zoran,Very nice and very inspiring.Thanks.
@Warfley,@cai,Good to know.Thanks.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: How to create form in thread?
« Reply #6 on: May 20, 2023, 02:03:55 pm »
Not sure about other platforms, but it's actually possible on Windows. You just should remember, that separate thread should have it's own message loop. Works for me in Delphi, except terminating. If Form2 is closed fist, application terminates properly, but thread doesn't exit, if Form1 is closed first and Form2.Close is called by it. Problem is most likely caused by posting PostQuitMessage to wrong message queue. No time to solve it now. Form1 becomes slow in Lazarus.
« Last Edit: May 20, 2023, 02:27:46 pm 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?

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: How to create form in thread?
« Reply #7 on: May 21, 2023, 04:22:38 am »
It is possible in Windows to have a thread with an own window and message loop.
This is described in the manual for wxWidgets, but it is disrecommended.
(Most GUI frameworks are strictly singlethreaded. I believe the reason is: The screen hardware is not reentrant and somewhere the access must be serialized anyway. So multithreaded access has no performance advantage)

It should be possible in Pascal to have a local procedure in a thread, which can be called by the main (GUI) thread, but can access local variables of the thread, and I think this fulfills the same purpose.

The following code works in fpc trunk, however it is experimental and I do not know if this is correct code.
{$modeswitch functionreferences} is required.

Code: Pascal  [Select][+][-]
  1. type pref=reference to procedure;
  2.  
  3. procedure TMyThread.Execute;
  4. var
  5.   newStatus: string;
  6.  
  7.   procedure test;
  8.   begin
  9.     Form1.Caption := newstatus; //fStatusText;
  10.   end;
  11.  
  12. begin
  13.   newstatus := 'TMyThread Starting ...';
  14.   Synchronize(pref(@test));
  15.   .....
  16.  
« Last Edit: May 21, 2023, 11:56:32 am by Peter H »

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: How to create form in thread?
« Reply #8 on: May 21, 2023, 03:20:52 pm »
My idea about why it would be needed - to keep UI as responsive as possible, while avoiding lots of Application.ProcessMessages calls. Because at the end it's weak point in event-based UI design. It looks unnatural. You should know, that such design is inherited from Win3.x era, when there wasn't hardware multitasking. First it was carried to Win9x/NT to keep code as compatible with Win3.x, as possible. It was also good idea, because such design was automatically minimizing CPU load. Because when application's main thread is always blocked, while it has nothing to do - then it doesn't consume any CPU cycles. But in modern situation, I guess, it's always better to use threads for long tasks. What we talk here about - is situation, when we need two or more windows, that can perform some blocking operations, but shouldn't block each other. Yeah, such situation is more theoretical, than practical, because there are always other ways to achieve this goal.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

eljo

  • Sr. Member
  • ****
  • Posts: 468
Re: How to create form in thread?
« Reply #9 on: May 21, 2023, 04:16:54 pm »
My idea about why it would be needed - to keep UI as responsive as possible, while avoiding lots of Application.ProcessMessages calls. Because at the end it's weak point in event-based UI design. It looks unnatural. You should know, that such design is inherited from Win3.x era, when there wasn't hardware multitasking. First it was carried to Win9x/NT to keep code as compatible with Win3.x, as possible. It was also good idea, because such design was automatically minimizing CPU load. Because when application's main thread is always blocked, while it has nothing to do - then it doesn't consume any CPU cycles. But in modern situation, I guess, it's always better to use threads for long tasks. What we talk here about - is situation, when we need two or more windows, that can perform some blocking operations, but shouldn't block each other. Yeah, such situation is more theoretical, than practical, because there are always other ways to achieve this goal.
Here is an example that benefits from a threaded forms design, financial application with real time stock graphs (candle sticks etc) it would be nice to be able to allow multiple windows with out one tripping on the other's update times.

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: How to create form in thread?
« Reply #10 on: May 21, 2023, 04:30:20 pm »
So far I know, even GUI Frameworks which have reputation for speed and are used in games are single thread only.
Examples Qt, ImGui.
Blocking could possibly be prevented by using Queue() instead Synchronize().

However I am not specialist for this and not a professional, this are my uneducated thoughts and I am experimenting with multithreading currently.

Mr.Madguy

  • Hero Member
  • *****
  • Posts: 844
Re: How to create form in thread?
« Reply #11 on: May 21, 2023, 05:55:20 pm »
Here is an example that benefits from a threaded forms design, financial application with real time stock graphs (candle sticks etc) it would be nice to be able to allow multiple windows with out one tripping on the other's update times.
Still possible without having thread for each form. Only scenario, I can think about - is some sort of program inside program, like plug-in for example.
Is it healthy for project not to have regular stable releases?
Just for fun: Code::Blocks, GCC 13 and DOS - is it possible?

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: How to create form in thread?
« Reply #12 on: May 22, 2023, 01:07:53 pm »
I rewrote the multithreading example to do the trick.
This requires trunk compiler.
The procedure "test" can also be implemented as anonymous procedure. (However, codetools are not fully ready for this yet)

This approach should also work with current compiler, but then the procedure "test" must be global and cannot access local variables.
Breakpoints in the procedure "test" do not work with FPDebug, but work with GDB.

I am not experienced with freepascal, but I think this should work and solve the problem.
The procedure "test" is in the codeflow of the thread, but is executed asynchronously and concurrent by the main GUI thread. Usually threads must do time critical or timeconsuming work and only want to wait, if nothing is to do. Therefore I believe it is an advantage, when the GUI stuff is not done by the sub-thread, and it is an advantage when the sub-thread does not need a message loop.

Code: Pascal  [Select][+][-]
  1. {$modeswitch functionreferences}
  2. {$modeswitch anonymousfunctions}
  3. // {$warn 5036 off}// "Warning: (5036) Local variable "$Capturer" does not seem to be initialized"
  4. {$macro on}
  5. {$define proc:= procedure}
  6. {$define func:= function}
  7. {$define b_ := begin}
  8. {$define e_ := end}
  9.  

Code: Pascal  [Select][+][-]
  1.  
  2. type
  3.   TProcref = reference to procedure;
  4.  
  5. procedure TMyThread.Execute;
  6. var
  7.   newStatus: string;
  8.   Form2:TForm=Nil;
  9.  
  10. procedure test;
  11. begin
  12.   Form1.Caption := 'Form1 '+newstatus;
  13.   Form2.Caption := 'Form2 '+newstatus;
  14. end;
  15.  
  16. begin
  17.   newstatus := 'TMyThread Starting ...';
  18.   //"proc" is a macro and expands to "procedure". This avoids a Codetools Problem.
  19.   Queue(proc begin
  20.     Form2 := TForm.Create(Nil);
  21.     Form2.Show;
  22.     Form2.Caption := '************ This is a Test';
  23.   end);
  24.   sleep(1000);
  25.  
  26.   Queue(TProcref(@test));
  27.   while (not Terminated) and (True {any condition required}) do
  28.   begin
  29.     //here goes the code of the main thread loop
  30.     newStatus := 'TMyThread Time: ' + FormatDateTime('YYYY-MM-DD HH:NN:SS', Now);
  31.  
  32.     if NewStatus <> fStatusText then
  33.     begin
  34.       fStatusText := newStatus;
  35.       Queue(TProcref(@test));
  36.     end;
  37.  
  38.     sleep(50); // fStatusText alternatively the thread may wait for an event. E.g., external I/O
  39.   end;
  40.   Synchronize(proc begin Form2.free; end);  //make sure the GUI Queue is empty, before procedure exits
  41.   end;
  42.  

Remark: Queue() should only be used, when short term blocking of the thread must be prevented.
In most other cases Synchronize() is mostly the better choice.


« Last Edit: June 02, 2023, 06:52:20 pm by Peter H »

Warfley

  • Hero Member
  • *****
  • Posts: 1499
Re: How to create form in thread?
« Reply #13 on: May 22, 2023, 01:43:02 pm »
Here is an example that benefits from a threaded forms design, financial application with real time stock graphs (candle sticks etc) it would be nice to be able to allow multiple windows with out one tripping on the other's update times.
Well you can write asynchronous code without having threads. Look at my stax pong example, this does networking on the same thread as the GUI by just executing the networking Code in between events and never block the thread.

I think that generally threading should be avoided wherever possible. Especially networking does not require another thread, because all that time you wait in networking you don't wait for computations, you wait for the Network data to arrive. There is no reason to just handle the form events during this wait period (not just with stax, application.processmessages and non blocking reads would also work)

For example Javascript webapps usually use websockets or ajax to hot load data, yet Javascript is completely single threaded. A well designed event scheduling system is completely sufficient
« Last Edit: May 22, 2023, 01:45:00 pm by Warfley »

Peter H

  • Sr. Member
  • ****
  • Posts: 272
Re: How to create form in thread?
« Reply #14 on: May 22, 2023, 01:56:19 pm »
Threads can use another CPU core and can run truly parallel and use 100% CPU without throttling the GUI.
Of course they should not be used without reasons, but there are reasons.
« Last Edit: May 24, 2023, 08:43:56 am by Peter H »

 

TinyPortal © 2005-2018