Difficulty using threading

RandomCoder

Well-Known Member
Licensed User
Longtime User
As a finishing touch to my application I wanted to add a pop-up panel that displayed the message "Searching For Files ..." where the dots " . " increased to ten and then reset again to signify that the application was busy and not just hung.

This is because my application is populating an array list with all the .rtf files located in a certain directory (there are thousands) and parsing the name then storing this in a SQL table.

I thought that the best practice would be to use the threading library and then call the sub to display the label as a seperate thread but alas I can't seem to get it to work properly. Even setting the priority to 4 (max) it still fails to display the panel until after the results have been found.

Atached is my program with a couple of sample files. I've added a sleep(3000) in the search routine to produce a slight delay which should be enough time for the label to be seen before the results are displayed.

No doubt it's me that is doing something wrong but I have no idea what. I've trying using DoEvents and forcing the label to the front. I've waited for the thread to start before calling the search routine and even waited until the label visible property is set but nothing has worked.

Please help!!!

Thanks,
RandomCoder

PS How many values can an array is hold? Is there a limit or is it purely memory dependant?
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
Did you have a look at the ProgressBar object in the ControlsEx library ?

I've used the ProgressBar before but for this application I have no way to measure how long the operation will take and so using a ProgressBar is not really practical.
And in any case I like to learn new things and so thought that this was the perfect opportunity to use the Threading library for the first time :)

Regards,
RandomCoder
 

kolbe

Active Member
Licensed User
Longtime User
I've just done a similar thing using the progress bar. I haven't looked at your code but it seems best to do the opposite to what you are doing. Thread the code that is doing the work and not the progress indicator. This way the GUI thread stays active while the work is done in a new thread. You will have to do a thread.wait and thread.reset on any global variable that is used in the thread both in the threaded code and out. Use the thread_event to update your progress indicator if you are updating it from the threaded code.

Hope this helps.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
kolbe,

Thanks for the advise, I'd not thought of taking that approach but am confident that it will work. I'd tried all manner of other things and was about to give up. This gives me another avenue to try and go down.

Regards,
RandomCoder
 

agraham

Expert
Licensed User
Longtime User
Kolbe is correct. You must do the work in the thread so that the main program is free to process messages and keep the GUI alive. You won't be able to display progress if the main thread is wrapped up doing work as it will never get the chance to process messages to draw the GUI.

There's too much of your sample code to delve into to find out what you are doing but looking at DisplayNotice

1) You must not try to update GUI elements from a thread. You need to use a thread event as events run on the main form thread which is the only thread allowed to update the GUI. To use thread events in a FormEx you need to invoke FormEx.EnableThreading.

2) DoEvents is of no use in a thread as DoEvents processes any windows messages waiting in the thread's message queue and only the main form thread has a message queue.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
Kolbe is correct. You must do the work in the thread so that the main program is free to process messages and keep the GUI alive. You won't be able to display progress if the main thread is wrapped up doing work as it will never get the chance to process messages to draw the GUI.

I soon realised this and that is why I attempted to circumvent it by increasing the threads priority and added the DoEvents to get my label to display.

There's too much of your sample code to delve into to find out what you are doing but looking at DisplayNotice

I'm sorry about that, it's very nearly complete and this is really a nicety so as to show the user that the program hasn't just hung. The Main module is complete except for the DisplayNotice sub right at the end (this is what I'm currently working on). The Search module is almost complete, just needs commenting and regions adding to make it more understandable. The bit that calls both the search and the notice to display is contained in Sub BtnTsSearch_Click.

1) You must not try to update GUI elements from a thread. You need to use a thread event as events run on the main form thread which is the only thread allowed to update the GUI. To use thread events in a FormEx you need to invoke FormEx.EnableThreading.

I had misinterpreted the help file and thought that "Threads can be useful to keep the user interface alive" meant that I could display messages/status to the user. I now understand that the thread should be running the process intensive part whilst the main B4PPC program carries on with its own business. Thank you for clarifying this.

2) DoEvents is of no use in a thread as DoEvents processes any windows messages waiting in the thread's message queue and only the main form thread has a message queue.

I am but a novice, eager to learn new things and happy to be told where I am going wrong :sign0089:

BUT... I have tried to run the Search module in a new thread and now I'm being told that the "Object Ref is not set to an instance of an Object" - you know the one! I've had this many times before and can usually fix the problem myself but nothing I do will get around the problem. I've tried using AddObject to add the conection, command and datareader objects that I require before calling the thread to start but I still recieve the same message. Where am I going so wrong :BangHead:

As always, any help is very gratefully appreciated :sign0085:

Regards,
RandomCoder
 

agraham

Expert
Licensed User
Longtime User
IWhere am I going so wrong
It's unlikely to be directly to do with threading unless it is something to do with the Thread object itself. The code ought to run identically whether threaded or not as long as it doesn't access user interface elements which is a no-no for a thread. This is a .NET requirement and not a Basic4ppc limitation. If you don't sort it then post a runnable version that errors and I'll take a look.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
Thanks Andrew.
I'm going to persevere a little while longer as I find that I get a better understanding of where I went wrong if I'm able to sort it myself. My main concern was that maybe the Threading library didn't allow me to call a sub in another module that used Objects from the SQL library. But if this is feasible then I am obviously overlooking something and its no doubt very simple - I've just not found it yet :)

Thanks,
RandomCoder
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
Usually when I'm doing something wrong I go right back to basics and make a test app to explore where it is that I've made the mistake.
This time I can't find it :(

The call to create a New SQL command type object always fails. I've tried adding the object in the IDE and in the code but it always faults with the Object Reference not set to an instance of an object type fault. Don't know what I'm doing wrong or if it is a SQL/Threading compatability issue :confused:

Please cast your experience on the issue. Attached is what should be a very simple example which demonstrates the trouble I'm having. I have at least worked out that it has nothing to do with using modules :icon_clap:

Regards,
RandomCoder
 

agraham

Expert
Licensed User
Longtime User
The problem, believe it or not, is to do with AutoScaling! The New code on a Command object looks for the thread AutoScaling data that is not present on a Threading library thread, it is only present on the main forms' thread. For the time being keep the New invokes outside the thread and just do the call to perform the SQL query and any subsequent (non-GUI) processing in the Thread.

I'll ponder this a bit more tomorrow - maybe I need to replicate the thread data but I'll see.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
The problem, believe it or not, is to do with AutoScaling! The New code on a Command object looks for the thread AutoScaling data that is not present on a Threading library thread, it is only present on the main forms' thread. For the time being keep the New invokes outside the thread and just do the call to perform the SQL query and any subsequent (non-GUI) processing in the Thread.

I'll ponder this a bit more tomorrow - maybe I need to replicate the thread data but I'll see.

How do you work these things out? :sign0188:

I wouldn't worry about 'replicating the thread data' (whatever that involves) as your work-around of placing the New call outside of the thread, if documented in the help, shouldn't cause anyone a problem.

I'm actually quite releaved that it isn't something really silly that I have just overlooked :)

Thanks,
RandomCoder.
 

agraham

Expert
Licensed User
Longtime User
How do you work these things out?
Genius, sheer genius! :), aided by nearly forty :( years of experience. Actually, in this case, by looking at the actual C# code for New1 to see what it was doing that might cause the problem - and finding it!

By the way

Do
...
Loop While ShowWait

or

Do While ShowWait
...
Loop

is rather tidier than using Goto although it compiles down to much the same thing in the end. Note the subtle difference between the two constructs. The first always executes at least one iteration of the looop code, the second runs straight on if it can.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
Andrew I have great respect for the amount of effort you put into helping others and hope that in the future I too will have the knowledge and experience that you have.
But for the moment I must continue with my journey of self development.

And this brings wth it a new problem....

An error occurred on sub search.updatetable.
Line number: 121
SQLCommand.ExecuteTable( "TblFiles" , 0 )
Error description:
Controls created on one thread cannot be parented to a control on a different thread.
Continue?

At a guess I need to dispose of the table (if present) and then create a new one each time I want to use the ExecuteTable command. Is this the correct way?

This fault only occurs when I attempt to start the thread for a second time. The first time I run it, everything works perfectly. I allow the sub that the thread started to run through its entirity and I've even used Thread.Abort to ensure that it is stopped. When I come to run the same thread again I get the error message. What troubles me is the fact that I'm not actually creating a new control with the ExecuteTable command, according to the help file it just empties the existing table before loading new values and so should I be getting this message?

Thanks once again,
RandomCoder
 

agraham

Expert
Licensed User
Longtime User
At a guess I need to dispose of the table (if present) and then create a new one each time I want to use the ExecuteTable command. Is this the correct way?
I would guess that too. Every time you do Thread.Start you create a new thread which is a different thread to the previous one on which some components of the Table were created by ExecuteTable.

However there is a deeper problem here do do with acessing GUI controls on a different thread to the main thread. A Table is a GUI control and should not really be accessed from a thread. You may get away with it on the desktop but a device will probably complain as it has strict checks for cross-thread access that the desktop doesn't have. You should really be using ExecuteReader in a thread but try Dispose and New and see if it works for you. Does your SQL query really take that long to run that it needs to be on a thread?

What troubles me is the fact that I'm not actually creating a new control with the ExecuteTable command, according to the help file it just empties the existing table before loading new values and so should I be getting this message?
It's probably not complaining about the Table but about some sub-control of the Table that is recreated when you invoke ExecuteTable.
 

RandomCoder

Well-Known Member
Licensed User
Longtime User
However there is a deeper problem here do do with acessing GUI controls on a different thread to the main thread. A Table is a GUI control and should not really be accessed from a thread. You may get away with it on the desktop but a device will probably complain as it has strict checks for cross-thread access that the desktop doesn't have. You should really be using ExecuteReader in a thread but try Dispose and New and see if it works for you. Does your SQL query really take that long to run that it needs to be on a thread?

I promise that I've read the help file (several times over) but thought that I was OK to update the content of a table and some ComboBoxes since the Form these reside on is not shown. I wait until the Thread is finished before attempting to show the Form.

The most lengthy part of my program is when I first populate an arraylist with all the .rtf files from a certain location and then parse the filenames into a SQL table. I only do this if the user wishes to search for a particular record so most of the time the code is not used. I also keep the arraylist so that should the user wish to find a second record then I don't have to do the searching for .rtf's all over again. To get round my problem I only use threading the first time a search is requested. After that the SQL table of filenames has already been created and it is just the content of the comboboxes that need to be reset and luckily this doesn't take very long.

Thanks for all the help,
RandomCoder
 

agraham

Expert
Licensed User
Longtime User
thought that I was OK to update the content of a table and some ComboBoxes since the Form these reside on is not shown. I wait until the Thread is finished before attempting to show the Form.
The GUI controls, including Forms, belong to the thread that created them and no other thread should touch them. Whether a Form is shown or not doesn't matter, who it belongs to does. An application can have many Forms but they all (in Basic4ppc) belong to the one main thread that starts on the first Form.Show. All events in Basic4ppc run on the main thread for this reason so you use thread events to run code that wants to update the GUI because that code will run on the correct thread. Your thread is supended until the main thread has run the event code then the thread resumes so it looks like a normal Sub call but takes a lot longer to execute.
 
Top