Login ProductsSalesSupportDownloadsAbout |
Home » Technical Support » DBISAM Technical Support » Support Forums » DBISAM General » View Thread |
Messages 1 to 10 of 22 total |
OT: Problem with threads |
Tue, Oct 30 2012 12:45 AM | Permanent Link |
Adam H. | Hi,
We have an unfortunate situation with one of our customers where their SMTP server takes over 10 seconds to accept each email. As the application I have created for them can send a number of these messages off at a time - the users are finding that the application runs very slow. As a work around, I am attempting to implement a thread function where the application can call the sendmail thread, and then allow the user to continue with whatever they want to do until the mail has been sent in the background. I've managed to implement the DBISam part of the application pretty easilly. The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed. I've tried synchronise, postmessages, etc - but can't seem to get this to work. If anyone is able to help - I have put an example of my project in the Binary section of the DBISam newsgroup under [ThreadProject]. I have eliminated all DBISam code out - so I know that DBISam is not the issue here, but was hoping someone with thread knowledge may be able to assist or point where I've gone wrong? Cheers Adam. |
Tue, Oct 30 2012 5:17 AM | Permanent Link |
Roy Lambert NLH Associates Team Elevate | Adam
Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg> This chunk while not ST.Terminated do application.processmessages; essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc Here TF := TFormThread.create(nil); you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up. This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information? Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done. What you need to do is: 1. In your thread definition add two extra parameters onto the constructor - respondmessage and respondhandle. These will be a message to be used in the WM_USER block and the handle of the form that will process the response message. 2. Inside the thread have a postmessage (NOT sendmessage that waits until it gets a response) send a message using respondmessage and respondhandle and set wParam and lParam to reflect whatever the progress is (eg have a counter internal to the thread which is implemented at the appropriate point and pass that back) 3. In the form that will process the progress message have a message handler eg procedure EmailsHaveBeenSkipped(var Message: TMessage); In that procedure you do whatever you want - display a dialog, update a progress bar etc If you use the above approach then its "easy". The only potential problem is that the Windows handle for the form processing the progress request gets changed. I implemented MainHWND := AllocateHWND(MainHWNDHandler); but this means that I then have to have a little procedure to process all messages - procedure TMainForm.MainHWNDHandler(var msg: TMessage); begin case msg.Msg of ProgressReport: FilterResult(msg); jmpRequired: ProcessJumpInstruction(msg); StatusMessage: ChangeStatusMessage(msg); PinToJump: AddToJumpButton(msg); OpenViewHistory: OpenViewHistoryForm(msg); SubsidForm: MakeSubsidiaryForm(msg); OpenMainForm: RemoteOpenMainForm(msg); InitialLogon: DoInitialLogon(msg); CloseSubsidTab: CloseNonMainTab(msg); StartPhoneTimer: DialedStartStopwatch(msg); StopPhoneTimer: DialedStopStopwatch(msg); AlarmsRunning: SetAlarmRunningStatus(msg); AlarmThreadCrash: AlarmsNotWorking(msg); CurrentAlarmCheck: FlipAniStatus(msg); AskForJmpVars: DoAskForJmpVars(msg); NewEMailReceived: if IsCreated[tagEMails] then PostMessage(frmHndl[tagEmails], bgMsgRefresh, msg.WParam, msg.LParam); OpenTfRMailViewer: ViewAttachedEMail; EMailsSkipped: EmailsHaveBeenSkipped(msg); else DefWindowProc(Self.Handle, msg.Msg, msg.wParam, msg.lParam); end; end; If you want to interact with the thread and pass information in from a dialog that's a bit more complex so I won't go into it unless you need it. Hopefully the above will get you going in the right direction. If not then ask more questions Roy Lambert [Team Elevate] |
Tue, Oct 30 2012 8:02 PM | Permanent Link |
Adam H. | Hi Roy,
> Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg> I have no problems with being ruthless. As I mentioned, I have little understanding about threads - so I'm thankful for your help! > This chunk > > while not ST.Terminated do > application.processmessages; > > essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc So effectively I should just not worry about it at all? I was just wondering how I go about Freeing and making ST nil again once the thread is complete. Can I do that in the thread itself. (After calling terminate)? > Here > > TF := TFormThread.create(nil); > > you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up. So I should have created a Datamodule instead. I've just made this change, and it's already working a ton better! > This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information? The dialog that appears should be called by the main thread, not the new thread I'm creating. Effectively all I'm wanting to do with the new thread is process something, and show a process bar of where it is up to. The problem I was having was that once a thread was running, if a user clicked on the "SHOW A MESSAGE" button, the application would effectively stop working completely. > Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done. > > What you need to do is: <snip> Thanks Roy! I'll need a bit of time to digest the rest of what you have just said, but will go through it and see what I can come up with. Once again - thanks very much for taking the time out to help! > If you want to interact with the thread and pass information in from > a dialog that's a bit more complex so I won't go into it unless you > need it. Thanksfully this isn't required at this stage. The simpler I can keep things, the better. > Hopefully the above will get you going in the right direction. If not > then ask more questions It has certainly helped a lot. Don't worry - I'll be back with more questions if I get stuck. Thanks again for your assistance! Cheers Adam. > 1. In your thread definition add two extra parameters onto the constructor - respondmessage and respondhandle. These will be a message to be used in the WM_USER block and the handle of the form that will process the response message. > > 2. Inside the thread have a postmessage (NOT sendmessage that waits until it gets a response) send a message using respondmessage and respondhandle and set wParam and lParam to reflect whatever the progress is (eg have a counter internal to the thread which is implemented at the appropriate point and pass that back) > > 3. In the form that will process the progress message have a message handler eg procedure EmailsHaveBeenSkipped(var Message: TMessage); In that procedure you do whatever you want - display a dialog, update a progress bar etc > > If you use the above approach then its "easy". The only potential problem is that the Windows handle for the form processing the progress request gets changed. I implemented MainHWND := AllocateHWND(MainHWNDHandler); but this means that I then have to have a little procedure to process all messages - > > procedure TMainForm.MainHWNDHandler(var msg: TMessage); > begin > case msg.Msg of > ProgressReport: FilterResult(msg); > jmpRequired: ProcessJumpInstruction(msg); > StatusMessage: ChangeStatusMessage(msg); > PinToJump: AddToJumpButton(msg); > OpenViewHistory: OpenViewHistoryForm(msg); > SubsidForm: MakeSubsidiaryForm(msg); > OpenMainForm: RemoteOpenMainForm(msg); > InitialLogon: DoInitialLogon(msg); > CloseSubsidTab: CloseNonMainTab(msg); > StartPhoneTimer: DialedStartStopwatch(msg); > StopPhoneTimer: DialedStopStopwatch(msg); > AlarmsRunning: SetAlarmRunningStatus(msg); > AlarmThreadCrash: AlarmsNotWorking(msg); > CurrentAlarmCheck: FlipAniStatus(msg); > AskForJmpVars: DoAskForJmpVars(msg); > NewEMailReceived: if IsCreated[tagEMails] then PostMessage(frmHndl[tagEmails], bgMsgRefresh, msg.WParam, msg.LParam); > OpenTfRMailViewer: ViewAttachedEMail; > EMailsSkipped: EmailsHaveBeenSkipped(msg); > else DefWindowProc(Self.Handle, msg.Msg, msg.wParam, msg.lParam); > end; > end; > > > Roy Lambert [Team Elevate] > |
Tue, Oct 30 2012 8:02 PM | Permanent Link |
Adam H. | Hi Roy,
> Looking at that bit of code you seem to be committing most of the sins in the book. I don't know how much is simple trying things out so I'll be ruthless <vbg> I have no problems with being ruthless. As I mentioned, I have little understanding about threads - so I'm thankful for your help! > This chunk > > while not ST.Terminated do > application.processmessages; > > essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc So effectively I should just not worry about it at all? I was just wondering how I go about Freeing and making ST nil again once the thread is complete. Can I do that in the thread itself. (After calling terminate)? > Here > > TF := TFormThread.create(nil); > > you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up. So I should have created a Datamodule instead. I've just made this change, and it's already working a ton better! > This bit in your post <<The problem I have is with the application halting whenever a dialog is supposed to appear until the thread is completed.>> if I'm reading it correctly is something I'm not clear on - is this dialog for information presentation of for the user to enter information? The dialog that appears should be called by the main thread, not the new thread I'm creating. Effectively all I'm wanting to do with the new thread is process something, and show a process bar of where it is up to. The problem I was having was that once a thread was running, if a user clicked on the "SHOW A MESSAGE" button, the application would effectively stop working completely. > Getting progress information back from a thread isn't difficult, however, the way a lot of people use - synchronise - is dire. It essentially blocks the main thread (ie the app) until synchronisation is done. > > What you need to do is: <snip> Thanks Roy! I'll need a bit of time to digest the rest of what you have just said, but will go through it and see what I can come up with. Once again - thanks very much for taking the time out to help! > If you want to interact with the thread and pass information in from > a dialog that's a bit more complex so I won't go into it unless you > need it. Thanksfully this isn't required at this stage. The simpler I can keep things, the better. > Hopefully the above will get you going in the right direction. If not > then ask more questions It has certainly helped a lot. Don't worry - I'll be back with more questions if I get stuck. Thanks again for your assistance! Cheers Adam. |
Wed, Oct 31 2012 4:11 AM | Permanent Link |
Roy Lambert NLH Associates Team Elevate | Adam
>I have no problems with being ruthless. As I mentioned, I have little >understanding about threads - so I'm thankful for your help! Ok goody - carte blanch - so to start - I hope your hiccoughs are better soon > > This chunk >> >> while not ST.Terminated do >> application.processmessages; >> >> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc > >So effectively I should just not worry about it at all? I was just >wondering how I go about Freeing and making ST nil again once the thread >is complete. Can I do that in the thread itself. (After calling terminate)? Threads have a property which can be set in their constructor or the Execute method FreeOnTerminate := True; which takes care of freeing the thread if you're not going to re-use it. I use the constructor. >> Here >> >> TF := TFormThread.create(nil); >> >> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up. > >So I should have created a Datamodule instead. > >I've just made this change, and it's already working a ton better! Good, but treat it just like any other class and use a unit instead. You can even define it in the form that uses it - after all its just another class declaration. >Thanks Roy! I'll need a bit of time to digest the rest of what you have >just said, but will go through it and see what I can come up with. > >Once again - thanks very much for taking the time out to help! If you need them I can send you a couple of fully working thread units. They won't compile at your end because they're calling all sorts of crap. Roy Lambert [Team Elevate] |
Wed, Oct 31 2012 7:35 AM | Permanent Link |
John Hay | Adam,
I know that synchronize has been rightly lambasted in the past, but it was improved in Delphi 6 (using postmessage instead of sendmessage) and there is now the option to use TThread.Queue (since Delphi 2006). I have found and posted a simplistic use of a thread updating a listbox that I wrote when I was first trying to understand threads. John |
Wed, Oct 31 2012 8:09 AM | Permanent Link |
Roy Lambert NLH Associates Team Elevate | John
>I know that synchronize has been rightly lambasted in the past, but it was improved in Delphi 6 (using postmessage >instead of sendmessage) and there is now the option to use TThread.Queue (since Delphi 2006). Most of the negatives I've seen posted about it relate to the fact that it "stalls" the main thread whilst synchronisation is occurring. Send rather than Postmessage would account for that. >I have found and posted a simplistic use of a thread updating a listbox that I wrote when I was first trying to >understand threads. Interesting little example - not sure about the formatting - it looks worse than Delphi's Roy |
Wed, Oct 31 2012 8:19 AM | Permanent Link |
John Hay | Roy
> Most of the negatives I've seen posted about it relate to the fact that it "stalls" the main thread whilst synchronisation is occurring. Send rather than Postmessage would account for that. Might be worth reading http://stackoverflow.com/questions/1806339/is-it-better-to-use-tthreads-synchronize-or-use-window-messages-for-ipc-betwe > Interesting little example - not sure about the formatting - it looks worse than Delphi's Project1,Unit1,Form1,Buton1,Button2 etc - clear as mud John |
Wed, Oct 31 2012 9:31 AM | Permanent Link |
Roy Lambert NLH Associates Team Elevate | John
>Might be worth reading > >http://stackoverflow.com/questions/1806339/is-it-better-to-use-tthreads-synchronize-or-use-window-messages-for-ipc-betwe Quite interesting especially the bit about using SendMessage to achieve "synchronisation". My world view is that you should only be bothered about "thread has started", "thread has finished" but I'm happy with "we've reached this point" but the minute you start having to pass more data than that I start to think it should be done in the main thread. I like some sort of visual indicator showing that the thread is still alive (often a progress bar) but I do consider that threads should be kicked out into the cold and allowed to get on with their own life! Roy |
Sun, Nov 4 2012 9:00 PM | Permanent Link |
Adam H. | Hi Roy and John,
I have made some considerable progress since our last discussion thanks to your help! Thanks very much! There are no more application.processmessages and calls back so far are done via postmessages. One thing I also learned that surprised me was that showmessage and messagedlg are thread safe - so I can actually raise one if I want within a thread for user intervention. However - my current situation has me wondering how best to handle exceptions within threads. I have gotten to a stage after searching the net where I am handling exceptions, but it's not freeing up the thread once it's completed. As a result - the application still things the thread is running, and since I don't want to close the app until threads have been completed (to avoid corrupting any data on writes) I'm not sure where to go to from here. I'm sure in this instance it's probably just one or two lines I've got wrong, but if someone is willing to take a look at my updated example, I would be forever grateful. I've whacked a copy of the update in the binaries NG, or alternatively from http://www.elevatesoft.com/forums?action=attach&category=dbisam&id=dbisam_binaries&msg=1803&attach=1 Thanks again for all the assistance! Cheers Adam. On 31/10/2012 7:11 PM, Roy Lambert wrote: > Adam > >> I have no problems with being ruthless. As I mentioned, I have little >> understanding about threads - so I'm thankful for your help! > > Ok goody - carte blanch - so to start - I hope your hiccoughs are better soon > >>> This chunk >>> >>> while not ST.Terminated do >>> application.processmessages; >>> >>> essentially makes using a thread useless. Your code is going to sit there until the thread is terminated. Because of the application.processmessages the app will respond to button clicks etc >> >> So effectively I should just not worry about it at all? I was just >> wondering how I go about Freeing and making ST nil again once the thread >> is complete. Can I do that in the thread itself. (After calling terminate)? > > > Threads have a property which can be set in their constructor or the Execute method > > FreeOnTerminate := True; > > which takes care of freeing the thread if you're not going to re-use it. I use the constructor. > >>> Here >>> >>> TF := TFormThread.create(nil); >>> >>> you're creating a form in a thread. The VCL isn't thread safe so here you're pretty much guaranteeing its going to screw up. >> >> So I should have created a Datamodule instead. >> >> I've just made this change, and it's already working a ton better! > > Good, but treat it just like any other class and use a unit instead. You can even define it in the form that uses it - after all its just another class declaration. > > >> Thanks Roy! I'll need a bit of time to digest the rest of what you have >> just said, but will go through it and see what I can come up with. >> >> Once again - thanks very much for taking the time out to help! > > If you need them I can send you a couple of fully working thread units. They won't compile at your end because they're calling all sorts of crap. > > Roy Lambert [Team Elevate] > |
Page 1 of 3 | Next Page » | |
Jump to Page: 1 2 3 |
This web page was last updated on Thursday, April 18, 2024 at 10:42 AM | Privacy PolicySite Map © 2024 Elevate Software, Inc. All Rights Reserved Questions or comments ? E-mail us at info@elevatesoft.com |