Multiple "MainForm"

delphi package - getting into other processes
Post Reply
mschnell
Posts: 4
Joined: Fri Jun 11, 2010 9:25 am

Multiple "MainForm"

Post by mschnell »

I suppose madRemote could be the starting point to a solution of the following task:

Is there a decent way to create multiple instances of the VCL "application" and/or GUI Main Frame to allow for an application that has multiple independent "Forms" (each with it's own main thread), i.e. something like multiple applications that share a common memory space (and heap management) ?

The goal is to create a complex application that in one window needs to show rather complex GUI informations that come in from some threads and are passed towards the main thread and thus permanently generate heavy VCL activity and on another window some user interaction should be handled in a timely way (rather than being blocked by activity done in the other Window(s) ).

Starting from some level of complexity, simply doing "Application.ProcessMessages" is not enough any more as (1) this introduces additional overhead slowing down things even more and (2) with modern multi-core PCs, this does not allow the concurrent frames to use multiple CPUs which would greatly optimize the would-be concurrent VCL operations.

-Michael
madshi
Site Admin
Posts: 10749
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

madRemote won't help here. What you need to do is to rethink your application design. The VCL has one big problem: It's not thread safe. Which means that you can't create/show/use VCL forms in any thread but the main thread. So your application design should be like this:

(1) Main thread = GUI thread. No processing. Only handling of user events etc.

(2) Secondary threads = worker threads. Started by the main thread, these threads are doing the heavy lifting, all the time consuming tasks.

That's the best design for an app IMHO, because this way the user interface is very responsive at all times, while the work in being done in secondary threads. The one big problem with this application design is that you need to synchronize acccess to global resources, and the secondary threads can't touch the GUI at all. The secondary threads will have to notify the main thread whenever the GUI needs to be updated in some way.

If you don't have experience with heavily multithreaded apps, you need to learn the synchronization rules first. Basically you can't access (read + write) any global resources (like global variables etc), anymore, without protecting the access by a synchronization protection. The best protection is usually to put the access to global resources into a critical section like this:

Code: Select all

EnterCriticalSection(GlobalVariableSection);
try
  localVariable := GlobalVariable;
finally
  LeaveCriticalSection(GlobalVariableSection);
end;
This way you are making sure that only one thread can access a global resources at a time.
mschnell
Posts: 4
Joined: Fri Jun 11, 2010 9:25 am

Post by mschnell »

madshi wrote:If you don't have experience with heavily multithreaded apps, you need to learn the synchronization rules first....
In fact we do have experience with multithreaed apps and the project in question already is a heavily multithreaded application that works fine (regarding the synchronization problems you mention).

Only now (as the CPU load on the application increases as the users want to manage more and more concurrent "real-world-objects" with our application on multi-Monitor PCs), the Windows meant for user input get unresponsive, as the multiple other active windows, dynamically showing the "state" of the "real-world-objects" use up too much CPU resources. That is why we think about ways to provide another (maybe dedicated) core to the input Windows(s).

I see two possible ways to do this:

Either create an application that attaches two "Main" Windows (and appropriate "main"-threads, and with Delphi thus two VCL instances). I suppose that Windows does provide means to attach two active Windows to an application (in fact, AFAIK, madshi does provide means to allow for managing another Windows message queue by a worker thread), but I fear it's close to impossible to have Delphi manage additional VCL instances in an application. (Maybe creating another VCL instance in a DLL could be possible.)

Another way would be starting another Application that provides the input Window(s). This would ask for quite close communication between the Applications

Here madRemote might come handy, hopefully introducing a way to use instances of the user defined classes that hold the data of the "real-world-objects". (e.g. by managing (part of) the heap in a commonly used shared memory area) Of course here we need to take care of "mutual exclusive" type of synchronization regarding the common write accesses to those classes.

I hope I could make clear what I am up to.
-Michael
madshi
Site Admin
Posts: 10749
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

As I said, I don't think madRemote would be of much use here. madRemote allows you to copy function code and create threads in other processes. That's not really what you'd need. Instead you should be able to get long with simply win32 APIs like VirtualAllocEx and Read/WriteProcessMemory etc.

But I'm not convinced that this is all necessary in the first place.

What exactly does your main thread do which consumes so much CPU? Can't you offline some of that to another secondary thread?

In the worst case you could replace one of the CPU intensive VCL windows with a pure win32 window instead. That might mean a lot of work, but a pure win32 window can run in a separate thread without any problems (other than needing synchronization etc).

Another option would be to look into the VCL components which consume so much CPU usage. Maybe they can be tweaked so that they can offload their work to a secondary thread? Or maybe they can be tweaked in such a way that they can even fully run in a secondary thread (by not using VCL commands, but win32 APIs intead, although the parent window is really VCL)?

I would look into these options first.

Not sure if two VCL instances can run in the same process. Might be problematic, or not. I don't really know.
mschnell
Posts: 4
Joined: Fri Jun 11, 2010 9:25 am

Post by mschnell »

Thanks a lot for trying to help !

In fact I am not the creator of the said application, so I can't provide much information about the internals of same. But I do trust that my colleagues already did do a good job investigating ways of optimizing their program appropriately.

It seems to be fact that the only way to improve the behavior is to move the input window(s) into a separate thread (or application) from the backgound GUI activities.

Here I see two options:

1) an application with two "main-forms / main-threads". As I don't see that Delphi can do this in an exe, (as the VCLfunctions are not reentrant and you can't create multiple instances) maybe a DLL can provide a complete additional VCL instance and there is some way to share the instances of the business logic classes between the main program and the DLL.

Maybe creating an input Window without using the VCL could be doable (I'm sure my colleagues will not like this idea, though ;) ). Do you have a simple example on how to do this ?

2) Using a different process. Here the inter-process communication is the critical issue. I suppose the most handy way would be to create (some or all) instances of the business logic classes in a shared memory area. An "advanced" way to do this would be to use a memory manager that creates the heap in such an area (including auto-resize etc). I believe Delphi allows for alternate memory managers, so this might be possible if madRemote can't be used for sharing such objects.

Thanks again,
-Michael
madshi
Site Admin
Posts: 10749
Joined: Sun Mar 21, 2004 5:25 pm

Post by madshi »

mschnell wrote:1) an application with two "main-forms / main-threads". As I don't see that Delphi can do this in an exe, (as the VCLfunctions are not reentrant and you can't create multiple instances) maybe a DLL can provide a complete additional VCL instance and there is some way to share the instances of the business logic classes between the main program and the DLL.
Don't know, can't help with that.
mschnell wrote:Maybe creating an input Window without using the VCL could be doable (I'm sure my colleagues will not like this idea, though ;) ). Do you have a simple example on how to do this ?
All of madExcept's runtime windows are built by using pure win32 APIs. It's rather ugly code, though, and for programmers who're used to do VCL processing, it's painful as hell.
mschnell wrote:2) Using a different process. Here the inter-process communication is the critical issue. I suppose the most handy way would be to create (some or all) instances of the business logic classes in a shared memory area. An "advanced" way to do this would be to use a memory manager that creates the heap in such an area (including auto-resize etc). I believe Delphi allows for alternate memory managers, so this might be possible if madRemote can't be used for sharing such objects.
madRemote does not really help here. As I already said, multiple times.

"Automatically" sharing memory might be hard to do, although maybe possible, if you're writing a Delphi type memory manager replacement. However, if some of the VCL components you're using are bypassing the Delphi memory manager (e.g. using LocalAlloc, GlobalAlloc, VirtualAlloc), automation wouldn't work. You could manually share whatever is necessary, and build up some kind of communication between two processes, but that will be quite a bit of work - unless you can get along with only small amounts of communication.
mschnell
Posts: 4
Joined: Fri Jun 11, 2010 9:25 am

Post by mschnell »

madshi wrote:All of madExcept's runtime windows are built by using pure win32 APIs. It's rather ugly code, though, and for programmers who're used to do VCL processing, it's painful as hell.
I'll take a look at the source code.

Thanks a lot for trying to help !
-Michael
Post Reply