New to multithreading - help required
Good afternoon fellow forumites
I've been coding in VB on and off for the last ten years, but I've only now identified a requirement for multi-threading in my Windows Forms project.
I'm in the process of building a new application which makes it possible for the end-user to run a variety of different file imports. The application contains a single form which itself contains a combo box, a button, a multi-line text box and two progress bars.
The combo box enables the user to select the import that s/he wants to run, and the button enables him/her to then go ahead and run that import. The multi-line text-box is used to provide the user with feedback on what is actually happening at that particular point, whilst the two progress bars provide a graphical representation of how far the process has gone - the first progress bar tracks lets the user see how many files have been/have still to be processed while the second progress bar tracks the progress within the current file.
Each of the import processes is coded into a separate module, thus making it easier for me to isolate and troubleshoot any issues (as well as making the code much easier to read). The status text-box and progress bars are updated using a further separate Functions module, with the various functions being called at the appropriate points.
Whilst testing some of the larger modules, I started receiving a series of Managed Debugging Assistant exceptions being called. From memory, the majority of these were DisconnectedContext breaks - when I did some reading on these, everything I read seemed to suggest that the breaks were being caused by the fact that everything is being done on a single thread and therefore the UI isn't being refreshed often enough.
I've managed to work out how to have a particular module called within a thread, but as soon as I do that, I lose the functionality that provides either textual or graphical feedback to the end-user. Therefore, I need to understand how I code the calls to the functions that update the status box and progress bars so that the user is getting the feedback that s/he would want. At this point I should reiterate that I have never worked with multiple threads before, so the terminology is all very bewildering at the moment.
If anyone can give me any pointers on this, I'll be more than grateful.
TIA
Ian Henderson
Re: New to multithreading - help required
I'm not a great multi-threader but the way I've seen this done is to wrap your modules into "thread classes". the thread classes expose events "progress" events which are fired each time a thread carries out a bit of work. The form/module/whatever that fires off the threads can then consume those events and provide feedback to the user.
I think that might be a bit "old school" though and it wouldn't surprise me to hear that there are better ways nowadays.
Re: New to multithreading - help required
Quote:
Originally Posted by
FunkyDexter
I'm not a great multi-threader but the way I've seen this done is to wrap your modules into "thread classes". the thread classes expose events "progress" events which are fired each time a thread carries out a bit of work. The form/module/whatever that fires off the threads can then consume those events and provide feedback to the user.
I think that might be a bit "old school" though and it wouldn't surprise me to hear that there are better ways nowadays.
Thanks FunkyDexter
Please excuse what might turn out to be a silly question, but how do I wrap up those modules? For example, on my main form, the code behind the button fires a CASE statement which looks to see which value has been selected in the combo box and executes the main method in the appropriate module.
For most of those selections, the case statement simply fires moduleName.methodName(). As an experiment, I tried changing that for one of the case selections so that it looks like this:
dim ThreadName = New Thread (AddressOf moduleName.methodName)
ThreadName.IsBackground = True
ThreadName.Start()
On the face of this, this approach works a treat. However it's providing absolutely no feedback to the user which will ultimately alarm them because they'll think the process isn't actually doing anything. The only thing that will change when the code is running is the fact that the button disappears - I decided to hide the button until the user had made a selection in the combo box and the code for handling the visibility of the button appears at the bottom of the Click event because the intention is that it would be the last thing to be done.
Re: New to multithreading - help required
This is the exact scenario for which BackgroundWorkers were provided. I would look into those before handling the threading yourself.
Re: New to multithreading - help required
I don't have a VB example to hand but here's some code I've cribbed from a C#. Hopefully it'll point you in the right direction.
Create a "thread class" to do the work. This will need to inherit from BackgroundWorker:-
Code:
internal class GeneratePredictionsThread : BackgroundWorker
Have it override the OnDoWork method. this is what will fire when you kick the thread off:-
Code:
protected override void OnDoWork(DoWorkEventArgs e)
{
try
{
base.OnDoWork(e);
//do your work here. In your case that's probably a matter of calling the methods you've already written
}
catch (Exception exc)
{
exceptions.Add(exc);
}
}
You call the ReportProgress Method (inherited from BackGroundWorker) to report that your thread has made progress:-
Code:
ReportProgress(percentComplete)
So that's how you wrap your method and have it report progress back to the consumer. Now let's look at the consumer. First create an instance of your wrapper to do some work:-
Code:
using (generatethread = new GeneratePredictionsThread())
{
... //following snippets go here
}
Subscribe to the events your wrapper is going to generate when it reports progress:-
Code:
generatethread.ProgressChanged += new ProgressChangedEventHandler(t_ProgressChanged);
generatethread.RunWorkerCompleted += new RunWorkerCompletedEventHandler(t_RunWorkerCompleted);
Then kick off the thread:-
Code:
generatethread.RunWorkerAsync();
That will set your thread running. Whenever you issue a ReportProgress from within the thread class it will fire the ProgressChanged Event Handler (including the percentage complete in the event args) and when it completes it will automatically fire the RunWorkerComplete event.
Apologies for the C#. Hopefully another member will be able to provide a more readable VB version but I'm afraid I don't have one available. It also wouldn't surprise me to learn that there are better ways to do this now as this was written some time ago.
Re: New to multithreading - help required
Here's a tutorial Pradeep wrote in VB. It should be a lot easier to follow than my C# example.
Re: New to multithreading - help required
You also may want to look at using a custom DataGridView or ListView to display the threads' progress (you add a new row dynamically when a new thread is spun off)