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.
(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;
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).madshi wrote:If you don't have experience with heavily multithreaded apps, you need to learn the synchronization rules first....
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.
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.
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.
Don't know, can't help with that.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.
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: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 ?
madRemote does not really help here. As I already said, multiple times.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.
"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.