I'm writing some software that talks to external hardware via a dll (moving some motors and reading some values back). The calls to the dll are blocking and may not return for in the order of 10 seconds. The software performs a scan by moving the hardware, taking a reading and repeating for a number of points. One scan can take in the order of 30 minutes to complete. While the scan is running I would obviously like the GUI to be responsive and a live graph (in an MDI Child) of the incoming data to be updated at each point. Multithreading seems the obvious choice for this problem.
My question is, what is the best way to thread this and talk back to the main VCL thread to update the graph during a scan?
I currently have a single TThread descendant that performs the 'scan logic' and an array of doubles in the public var section of the ChildForm. I need to fill out this array from the thread but I don't know whether to use Synchronize or CriticalSection or PostMessage or some other method. Each time a new value is added, the main VCL thread needs to update the graph. Should I really have an intermediary object for the data that is a global var and access this from the Thread and the ChildForm separately somehow?
Use postmessage inside you thread and send messages to main form handle. Register one (or more) custom messages and write a handler for them.
Create a thread class, add a MainFormHandle property (Thandle or cardinal). Create thread suspended, set MainFormHandle with main form handle, then resume thread. When you have a new measure, assign data1 and data2 dword with some data from measure, then
In main form you have message handler:
If you need to send much more data from thread to main form, you can create an appropriate structure in main context for the whole measurement data, pass a reference to thread, let the thread write data and use messages just to tell main form new data position (an array index, for example). Use TThread.Waitfor in main context to avoid freeing data structure while thread is still running (and writing into memory).
I find that populating a
TThreadList
from the background thread, then posting a message to the main thread that there is a new item in the list, then processing the list in the main thread is simple and easily maintainable.With this method, you could store as many readings as you wanted in the list, and every time the main thread received a message, it would simply process all the items in the list at once.
Define a class for the readings, instantiate them, and add them to the list in the background thread. Don't forget to free them in the main thread when you pop them off the list.
If you want to invest a little more then a simple Synchronize call which by the way blocks the main thread, you can add a simple FIFO queue with messaging on top of it. The flow of data would be like this:
The code would look something like this:
the queue...
the data is put into the queue...
and processing...
The code is written without Delphi and checks so it can contain errors. I showed the example using my freely available thread safe queue and TAnyValue. You can find both here:
http://www.cromis.net/blog/downloads/
Also please note then I did not do any check if PostMessage was actually sent. You should check that in production code.
The simplest way to update the GUI from a thread is to use
anonymous methods
in conjunction withTThread.Synchronize
andTThread.Queue
.Passing values asynchronous often requires "capturing" a value.
When an anonymous method is using a variable, the reference to the variable is captured. This means that if you alter the variable value before the anonymous method is executed, the new value is used instead. Hence the need to capture the "value".
A more elaborate example can be found here,
synchronize-and-queue-with-parameters
, by@UweRaabe
.