![]() | ![]() Products ![]() ![]() ![]() ![]() |
Home » Technical Support » DBISAM Technical Support » Support Forums » DBISAM General » View Thread |
Messages 1 to 10 of 32 total |
![]() |
Thu, May 15 2014 2:21 AM | Permanent Link |
Adam H. | Hi,
Within my application I'm wanting a trigger to go and do some validation checks, and display to the user if any warnings they may need to be made aware of. The problem I have is that the validations will take some time by the time they complete. (Mostly running SQL statements, and displaying a message if certain criteria is or is not met). To use a basic example, if I was writing a dispatch program, and the user put in a record to dispatch a quantity of widgets, I would like to run a SQL that runs a query to ensure that there are enough widgets in stock, or show how many widgets the client has ordered before, etc but I don't want the user to be waiting for the procedure to finish. I've had some luck with playing with threads before (although it was a while ago now and my brain is hurting ![]() pass any information back or forth. In the past they have just been executing a fixed procedure more than anything else. So in this case I'm wanting to do something that will: a) Pass a variable so I can condition the SQL to limit to a particular stock code. b) Pass a variable of the amount of stock that is about to be outturned, and c) If the outturned amount is greater than the stock code - display a form or dialog saying so. d) I'm guessing I'm going to need to have some sort of check in place not to close the parent form until the thread is completed, or a way of terminating the thread safely if the form is closed. Likewise the popped up form will need to be closed too if it's created. Does this mean that the dialog, etc should be a variable within the calling form? Now with my past experience of threads, I recall: a) Never use VCL components or visible forms in a thread. In this case, I guess I can't use the thread to pop up a warning dialog? b) Is it OK to use synchronize? (I this I recall somewhere it was a good idea to avoid, but I can't remember). Is this a feasable idea, and if so what would be the best way to start going about this? (The above is a basic hypothetical - in my app there's a bit more to it, but I figure if I can do the above in a test application, I should be OK). Thanks to anyone game enough to step into my mess. ![]() Cheers Adam. |
Thu, May 15 2014 5:57 AM | Permanent Link |
Roy Lambert NLH Associates ![]() | Adam
Its actually not to difficult if you play by the rules: 1. ANY database components except the engine itself MUST be created within the thread 2. ALL variables used must be local to the thread or be read only, or atomic in size (best to stick to the first part) 3. DO NOT attempt to display any data or messages from within the thread 4. ALL communications with the main thread should be vis PostMessage, wrapped in a critical section or via synchronise (I stick with PostMessage) Those are the basic rules What I tend to do is override the threads constructor and pass initialisation parameters in through there eg TemsParams = record ReportBack: HWND; <<<<<<<<<<<<<<< LoopTime: integer; DoSend: boolean; DoReceive: boolean; IsolateSessionName: string; Archiving: string; end; The ReportBack handle in TemsParams is very important since a form's handle can change and if it does any messages you send will get lost. What I do is create a dedicated message handler with its own handle. constructor TBackgroundEMail.Create(emsParams: TemsParams); begin inherited Create(True); KeepOn := True; iCrashPoint := 0; iParams := emsParams; iSignal := THackSimpleEvent.Create; iArchiveControl := TStringList.Create; iArchiveControl.Text := iParams.Archiving; InitialiseService; FreeOnTerminate := True; Resume; end; If you're expecting a high volume of these threads to be created (ie loads per second) then you'll want to set up a queue handling system to save the overhead of creating the thread but as you're describing it I wouldn't bother As far as freeing the thread is concerned then if you stick with PostMessage its not to big a problem - the form won't be there and the message will simply be lost. Simple numeric data can be sent easily as wParam and lParam. If you want to send text you have to manage the get and free mem youself and pass the pointer in wParam or lParam In the example you give you could have a record which would have the database path, table name, part code, quantity ordered and pass that into the thread. Pass back the quantity on stock I do see a possible problem in that if two people place an order simultaneously(ish) then both could be told there's plenty of stock If you find yoourself struggling then if you want to send me a skeleton app I'll be happy to flesh out the thread part for you. Roy Lambert |
Thu, May 15 2014 6:14 AM | Permanent Link |
Matthew Jones | It's a complex subject, and my first thought was "I'd not start from here". But as
with travel directions, that's not a useful response. 8-) For your situation, I'd keep to my simple solution. Create a thread object who's sole purpose is to do the work you need. Create a class to contain the data it needs to work, and that you want it to return. Create a global variable and create an instance of the class for that purpose. Now, when you want the info, set up the global with the request information, and then create the thread and set it to kill itself on termination. The thread will mark the data in the class as completed, and in your form you have a timer to watch for the completed flag, and if so then it shows the results. The main thing is to keep the thread completely separate, and communicate only through an object that is uni-directional. Values that are set up from form to thread, and others that are used from thread to form. Then you don't have to worry about concurrency. The completed flag is set only when the thread is done setting the variables, and then the form knows that it can read them without issue. Don't use synchronise or anything like that, and you are much safer. Like you close the form, and the thread is still busy - who cares, it sets the info & flag, dies, and no one cares if your form never looks at it again. Only one more thing to worry about is setting many threads going, and you can handle that various ways. Easiest is to have the thread in a global variable, and have it set it to nil on termination. Only if the global is nil will you start another. And put an exception handler around the thread's Execute to make sure that happens. Since we are in DBISAM, you need to create a new Session and set it to auto-create its name. g_xThreadSession := TDBISAMSession.Create(nil); g_xThreadSession.AutoSessionName := True; g_xThreadSession.Open; szDatabaseLocation := g_xStartupConfig.DatabasePath; m_xWorkQuery := TDBISAMQuery.Create(nil); m_xWorkQuery.SessionName := g_xThreadSession.SessionName; m_xWorkQuery.DatabaseName := szDatabaseLocation; szDatabaseTemp := szDatabaseLocation + '\Temp'; g_xThreadSession.PrivateDir := szDatabaseTemp; /Matthew Jones/ |
Thu, May 15 2014 8:05 AM | Permanent Link |
Roy Lambert NLH Associates ![]() | Matthew
>It's a complex subject, and my first thought was "I'd not start from here". With the example Adam gave that was my first thought as well. To be honest my first thought went along the lines of "what plonker would design a despatch system that didn't have a stock control interface so the quantity on hand was known" but I relented because he did say it was a bit hypothetical. Thinking about it a bit more if the validation is complex and time consuming threads may make a lot of sense IF several of them can be fired off simultaneously all of which operate independently and the results get aggregated back in the main thread AND, most importantly, the user can actually do something useful whilst this is going on. Otherwise I'm in favour of leaving it in the main thread and showing some distractor - wheels turning, pigeon eating corn or something. This <<The main thing is to keep the thread completely separate, and communicate only through an object that is uni-directional. Values that are set up from form to thread, and others that are used from thread to form.>> has me slightly worried. From posts over on the Embarcadero forums it seems that unless the thing being read is read in an atomic fashion (eg boolean) its possible for it to be changing even as you read it. If that's correct then even uni-directional stuff (eg a string) may not be totally safe. I know you say set a flag to show the thread has finished with the implication don't read until then but I thought it was worth pointing out. The other option that neither of us mentioned is to write the data into a table - its a database app so you may as well get full value for your investment in DBISAM ![]() Doing it that way can to totally thread safe and you can poll for the results from the main thread or other places. Roy Lambert |
Thu, May 15 2014 8:22 AM | Permanent Link |
Matthew Jones | > 4. ALL communications with the main thread should be vis
> PostMessage, wrapped in a critical section or via synchronise To be clear, that is a list of three, not a PostMessage wrapped in a critical section. That way madness would lie! I use critical sections for more complex inter-thread comms when needed in my "global object". /Matthew Jones/ |
Thu, May 15 2014 8:49 AM | Permanent Link |
Raul ![]() | On 5/15/2014 2:21 AM, Adam H. wrote:
> Is this a feasable idea, and if so what would be the best way to start > going about this? (The above is a basic hypothetical - in my app there's > a bit more to it, but I figure if I can do the above in a test > application, I should be OK). Adam, I think you have some great suggestions by Roy and Matthew so i'll just add my 2 cents also. It's similar to Roy's in many ways. 1. Assuming you have reasonable amount of these checks you could just start one whenever needed and let it complete and terminate. You're essentially creating a new thread each time you need to do this but on the plus side you don't need to worry about thread comms too much. 2. Create a custom class inheriting from tthread and create a custom constructor like Roy suggested that accepts all your input parameters. You should keep track of it in main thread if it could run for a long time so you could terminate it if app is closed (and thread should check its terminate flag while running). 3. Create the thread using your custom constructor and that includes all the input params so no need to worry about passing any data in to the thread. 4. Thread execute needs to create all the dbisam objects (session,db+table/queries etc) and use them 5. In terms of notifying your user there are 3 ways to accomplish this depending on your requirements: 5.1 it's correct that you should not call any VCL dialogs from a thread but one exception is Windows MessageBox function (with nil owner window). It is thread safe to be called from thread for simple message display. I don't generally recommend it but it would mean you don't need to do any comms back to main thread at all. 5.2 You could use synchronize as it will run on main thread context but don't block when doing so (i.e. do a nonmodal window or just set some flags on main thread that then will trigger a message to be displayed later (using a main timer or some other mechanism)). 5.3 Thread OnTerminate event runs in the context of the main thread as well so you could this similar to previous point - so if you create an on-demand thread that runs and then dies this would be ok as well. 5.4 posting message back to main thread handle - best way since it's nothing shared model and most generic (though requires more setup to handle message and message handlers etc) Raul |
Thu, May 15 2014 9:08 AM | Permanent Link |
Matthew Jones | > I know you say set a flag to show the thread has finished with the
> implication don't read until then but I thought it was worth > pointing out. Indeed, the key is that you don't read until the writer has said all it ready. A Bool is atomic enough that this would work. As said in other messages, a critical section with getter/setters will stop anything more complex getting broken. It also depends on what you are doing of course - if you are reading the current temperature every second for display, a mis-read will only be there for a second. I'd assume the writer is doing the logging long term, so the "bad data" wouldn't be stored anywhere. > The other option that neither of us mentioned is to write the data > into a table - its a database app so you may as well get full value > for your investment in DBISAM ![]() > > Doing it that way can to totally thread safe and you can poll for > the results from the main thread or other places. That's where I'd move to, but it would be overkill perhaps. Most of my work nowadays seem to include a table with a work queue, and threads monitoring for jobs to do. The WebBuilder shopping system does this for order processing, and it calls the card process which is a database with work to do and threads to call the card people. A big step more work though, unless you've done it lots before. /Matthew Jones/ |
Thu, May 15 2014 9:10 AM | Permanent Link |
Roy Lambert NLH Associates ![]() | Matthew
>To be clear, that is a list of three, not a PostMessage wrapped in a critical >section. That way madness would lie! Technically, if I was mad, that would be a list of two - Number 1 - PostMessage, wrapped in a critical section Number 2 - synchronise Any intimation that I am on more than casual acquaintance with reality will be met with a firm denial. After all am I not a politician? Roy Lambert (written from his nice cosy padded cell) |
Thu, May 15 2014 7:01 PM | Permanent Link |
Adam H. | Hi Roy and Matthew,
Thanks very much for taking the time out to respond and help me. > With the example Adam gave that was my first thought as well. To be > honest my first thought went along the lines of "what plonker would > design a despatch system that didn't have a stock control interface > so the quantity on hand was known" but I relented because he did say > it was a bit hypothetical. I must admit - I got a laugh out of this. (Plus, I haven't heard the term 'plonker' used in decades <vbg>) My system isn't really what I described. Since it's so specific to the particular customers needs, I'd need to go into significant detail to try and explain what's really going on - so I figure it's easier to break it down into something that would be relatively common or easy to understand by most and work on it from a common understanding, especially because at least for the thread part - what's really going to be done for the customer is irrelevant. ![]() I must admit though, when I first got half way through your replies, the first thing that came to my mind was a segment from the IT Crowd: https://www.youtube.com/watch?v=_eUxpCG2n7o But thankfully after getting some caffeine, I think I understand the jist of it now. ![]() I'll go and create a small test based application and see what I can come up with. Cheers Adam. |
Thu, May 15 2014 9:32 PM | Permanent Link |
Adam H. | Hi Raul,
Thanks for your reply. That has certainly helped me to create a sample demo! Cheers Adam |
Page 1 of 4 | Next Page » | |
Jump to Page: 1 2 3 4 |
This web page was last updated on Wednesday, August 14, 2024 at 07:31 PM | Privacy Policy![]() © 2024 Elevate Software, Inc. All Rights Reserved Questions or comments ? ![]() |