Link to home
Start Free TrialLog in
Avatar of EricTaylor
EricTaylor

asked on

Delphi: Separate Thread

Our software, along with many other business-related actions, retrieves email which it summarizes key elements of in a client data set that the user can interact with in a variety of ways. A shortfall is that the user must still hit a "retrieve mail" button to get the mail since we don't want the main application thread stopped for a few seconds every minute checking to see if there is new mail and then updating the cds if there is any.

This sounds to me like a good candidate for putting the email retrieval in a separate thread, but I've never dealt with threads before, so I'm not sure where to begin or what the pitfalls are. I'd be happy for any foundational guidance on what to do/avoid in setting up the thread correctly, but in particular, would like to know how one handles potential conflicts between what the two threads are doing. e.g., what happens if the email retrieval thread is adding new messages to the cds at the same point that the user is double-clicking some other message in the data grid that is linked to the same cds? Thanks in advance for any help.
Avatar of developmentguru
developmentguru
Flag of United States of America image

I like to wrap potential conflict code in a procedure that, in turn, wraps the code in a critical section.  Both threads can call the procedure which is then thread safe.  If you want to add an item to the list, do it in this manner.  If you want to read an item on the list, do it in this manner as well.  The only side effect is that, while the user is double clicking on a line, the line may change.

  Keep in mind that if you want to update a client dataset that has visual controls tied to it that this will need to be done within a Synchronize call when it is called from any thread other than the main thread.

  Another approach I have used is primarily for loading data in a background thread that can the be used by the main thread.  Each time I create a thread, I add it's name to a list of the running threads.  Then the main application can call a function I create called ThreadIsRunning('MyThreadName') to test if the thread is running, and WaitForThread('MyThreadName') to wait until it is finished.  Since the thread is set to create a TClientDataset that is set to a local variable, well behaved code will not try to use the local variable until the thread is not running.  I further access the local variable via a public function.  This allows me to wrap all of the tests and waiting for the thread to finish inside the method, making it easy for code to be well behaved.  These calls are also using critical sections.

I hope that helps.
Avatar of Geert G
have you checked my articles ?
https://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/A_6613-Adding-threads-for-loading-data-in-background-to-a-delphi-application.html
https://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/A_239-Displaying-progress-in-the-main-form-from-a-thread-in-Delphi.html

in essence you will need 2 sets of data
> 1 cds for the thread
> 1 cds for use by the front end gui

the thread dataset can be a limited portion
i assume you will be retrieving mails and then copying them to the main cds a few at a time
at the time the thread is copying data to the main cds, it will freeze the front-end gui and trigger a data refresh in the end
You can use OmniThreadLibrary from Primoz Gabrijelcic.

http://otl.17slon.com/
Avatar of EricTaylor
EricTaylor

ASKER

sorry for the delay in reply to the various suggestions here... was away for a few days and am now exploring which of these comments seems to apply best to my situation. Thanks for the comments from all.
Being a complete novice when it comes to threading, I'm still struggling with parts of this. As I understand this, Geert is suggesting two cds's: one that the main thread uses to display info to the GUI, and one that the background thread writes new emails to.  That part makes sense, but I'm unclear about how to synchronize these two cds's. (Your examples in your articles may have shown this, but this area is so new to me that that I was stumbling in understanding parts of the examples.

DevelopmentGuru: is this where you are suggesting the critical section... at the point where these two data sets are synced? (Sorry, I've also never dealt with a critical section before, so please pardon my ignorance.)

Sinisav: Using an existing library might indeed make this process easier, but at the moment, this is so new to me that I'm not clear what parts I'd have to do and what parts the library does. I suspect this could become useful for other things as well, but I'm just at a loss for a beginning point: I'm not sure which of the many library features I'd need to explore to get the retrieved data into the cds used by the GUI.

Can anyone offer a bit of further guidance?
ASKER CERTIFIED SOLUTION
Avatar of Geert G
Geert G
Flag of Belgium image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
That is a good question.

  In the way I was suggesting there would only be one CDS.  Updating of the CDS would be done ONLY through routines that use the critical section.  All this does is act like a traffic cop... only one section of code... from any number of threads... can access it at one time.  Others attempting to access it wait until the critical section is not in use.

  If the call to those methods was done from a thread, and the CDS is connected through a client dataset to visual controls, then these calls would be done through Synchronize to avoid messing up things for the main thread.

  It would simplify things if the CDS was not connected through a DataSource to visual controls.  Then all you would need is an event that could cause the update of the display.

  If you go with Geerts way of doing things then yes, a critical section could also be used to sync up the two datasets.  Just remember that the critical section just acts like a traffic cop to protect the code using the shared resource, it does not do any updating itself.

I hope that helps to clarify.

P.S.
I applaud a person for trying to understand the bare metal programming over, or at least before, just trying to use a library.  Using libraries is great, but only after you understand enough about how they work.  Too many programmers these days, when get to a point where they do not understand how to move forward, assume they either need to 1) obtain controls to do the work for them or 2) that it cannot be done.  How sad for them.
Geert /DevelopmentGuru: Thanks to both of you for helpful answers. I've been momentarily derailed fixing bugs from a release, but will be back to this early next week. I'm putting off accepting a solution until I implement a a bit of this and can more fairly say which pieces contributed the most to what works for my situation. But you've both shed helpful light on this. Thanks again.
development guru
visual controls don't call a critical section before accessing the data
not for edit, and not for display

so that's why you need a separate dataset

the gui wll hang and cause av otherwise
there is a cleanup process on this site,
if no activity mlmcc will close this
Geert,

  That is a very good point assuming that the delay would be more than, say, a millisecond or so.  It was my understanding that this was a process that would be retrieving, at most, a few emails at a time.  Since the retrieval process is housed in a separate thread, and should be done with it's fetch before ever attempting to notify the client of the update, it should work nearly invisibly.  It is a very good thing to point out the possibility of locking the UI though, I appreciate it.

  The primary thrust of what I wrote was NOT to use the visual controls, in a data aware manner, with this side thread.  It is common practice to use critical sections in any thread, including the main thread, when the situation warrants it.  Here is an MSDN article that speaks to the use of critical sections within the main thread, and side threads, and how to debug issues that you may run into.
i still think you need 2 datasets
if not the thread can accesses the dataset while the gui is editing
since the gui doesn't use a critical section to access the same data

this has nothing to do with timing
it's just basics : a resource is only thread safe if every procedure accessing the resource is locked by a synchronization object (in this case a TCriticalSection)
Thanks both to Geert and DevelopmentGuru for the input. DevelopmentGuru, I found your introducing me to the critical section interesting, but given the gui interface need to interact with the cds extensively (as would be expected with email), Geert's solution seemed to fit better. But I appreciate the helpfulness from you both.