First off… a Thread doesn’t work here, there is too much interaction with the GUI required… I tried and it would require me to totally re-engineer quite a few custom controls
So here is the situation.
I have a custom control that takes a text file (which could be VERY large), and parses it in to columns of valid data.
as each column is extracted, it calls an event which notifies the window that there is a SINGLE row to be dealt with
If a method on the window decides that returned data is invalid, it signals the custom control to stop processing.
What I need to do is have a BUTTON on that window that can also abort the process… but so far that button doesn’t seem to fire until the custom control is completed, or programmitcally aborted.
I CAN do it by using the “EVIL” app.doEvents…
but wouldn’t that also get an AppleStore reject?
Like I said there is way way too much interaction with the GUI, and using “timers” to intervene would slow things down too much, as this process really needs to run as fast as possible… I’m not worried about the user being able to abort a short process, but it is when there are millions of records, and they decide they don’t want to wait .
As of now… the App.Doevents… added maybe 0.001 seconds to my test, but gives me the ability to abort at will.
Properly coded, a thread will use almost 100% of the CPU (and thus be just as fast as non-threaded code) but still allow the foreground UI to be responsive.
It’s a pain the first time you do this, as it seems like a lot of trouble, but once you’ve done this sort of design pattern once you will see it’s really the way to go.
Norman, I NEVER said that… What I DID say, it that the amount of required UI interaction during the process that “should” be given to a thread is extensive… it has to update logs (textarea, progressbars, and sometimes even ask the users decision before proceeding).
So even if I were to put it in a thread, I doubt there would be any “real” benefit as the extra band-aids to keep the UI up to date would add to the amount of time required to complete the task. And in this situation, the GUI is locked as far as the user interacting with it down on purpose with the exception of a single “ABORT” button. There is nothing they can or should be doing until it completes, sometimes this is a few seconds, but if they hand it a 10gig file, it will take a while.
FYI… my biggest issue with a thread right now?
The window has my custom control on it (a non-visual control)… but the thread (also on the same window) says it doesn’t exist, yet
My control has a method called “PROCESS”
if I call “PROCESS” from then “OK” button, it does what I expect
If I replace the call with “THREAD1.RUN” and place the PROCESS call in the RUN event, then all of a sudden the custom control isn’t available anymore.
“PROCESS” deconstructs a file and raises an event when it has a “packet” or record to be processed… similar to I’d guess an HTTP socket except the data is from a local file… And SPEED is required
Create a Timer and properties to hold an index and the data you want processed. When you need to process, start the Timer with a minimal period, like 1. At the start of the Timer.Action, record the Ticks then resume with the next index. Keep looping until the elapsed time is >= 3 ticks. Record the next index and exit. When the loop is finished, stop the Timer. This sort of emulates what a Thread does but keeps your UI accessible and responsive.
Why not return true from the event to stop the process? Then the abort wouldn’t be immediate but on every line you would have the opportunity to check a boolean, to see if the abort button was pressed.
Never the less… In my experience on windows, methods running longer than 5 or 6 seconds result in “The App (Not Responding)”. The only alternatives I know of are a timer or a thread.
I have reengineered the entire “custom control” into a single “method” which is basically a For/Next loop with a bunch of processing inside. During that process if messages are generated they are placed in a queue
A timer on the calling window executes this action ever 1/2 second or so
getMessage // get any messages from the queue and move to the TextArea in the GUI
If Thread1.state=Thread.NotRunning Then
btnCANCEL.caption="Done"
Me.Mode=timer.ModeOff
End If
What I’m confused about… is what do I need to do to yield to the GUI???
Also, because I know you don’t want the process to feel long, I usually use a period of 150 on a timer that’s sole purpose is to pass messages out of a thread, the UI stays active and everything feels smooth.
Another technique I’ve used is to make thread-safe control subclasses. Imagine a TextArea or Pushbutton subclass which has the normal methods which (when called from a thread) simply set up an internal timer callback to do the right thing. With these in place, you can write your threaded code without worrying about UI issues. It’s very clean and makes a lot of sense once it’s working.
(I really should put these classes up on GitHub or something).