Click here to Skip to main content
15,882,113 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
See more: , +
SOLVED - PLEASE SEE COMMENTS BELOW IF YOU ARE INTERESTED IN HOW THIS WORKED OUT, SINCE IT IS A 2-PART SOLUTION, BOTH OF WHICH ARE POTENTIALLY USEFUL TO OTHERS.

Often, users will go to my program's "import data" window to initiate an import of data from a file or spreadsheet to our database. Depending on the size of the import, though, this can take up to 2 minutes.

Therefore, to reduce this time, I have decided to get a head start on the predictable aspects of the import process during program initialization, by using a BackgroundWorker.

As the data import progresses, the data import classes are busy raising the BackgroundWorker.ReportProgress event, passing in a state object in the ProgressChangedEventArgs . The state object has all the data to show a ProgressBar and some other state like number imported, an icon, popup message, etc., but there is no UI (yet) to display this progress.

But then if the user is quick enough, they can get to the "import data" window before the background import is done. It is still in progress in the BackgroundWorker.

So when that window loads, I "tune in" to the state of the import process, by handling the ProgressChanged event of the BackgroundWorker. This keeps the user amused while the process completes.

By the way: Since this is a Windows Forms application, I can tell you that I have had lots of trial-and-error fun with cross-thread errors trying to update UI controls from the BackgroundWorker thread. I finally gave up on all that and decided to try and use the ProgressChanged event instead.

Back to the sad story: However, at this point, the user may now decide to re-import, or change some properties that were preventing the success of the import, and proceed to initiate the import "manually", as they used to do before I implemented this BackgroundWorker idea.

So now the program is doing another data import, and of course I want to use the same classes that the BackgroundWorker used, but this time, it's going to be done in the UI thread.

So the BackgroundWorker.ReportProgress event that my import classes were raising to the BackgroundWorker need to be handled instead by the UI thread.

I have this foggy notion that a Delegate could be used to report progress to either the UI thread or the BackgroundWorker thread, but I don't have a lot of experience with using them.

So here's my question: How can I use a Delegate to send this progress to the BackgroundWorker if it's running, but to the UI if the process is initiated there?

Or, do I need a to abandon the BackgroundWorker idea and teach myself how to use something like the more complicated Event-based Asynchronous Pattern as explained in the MSDN article entitled "Event-based Asynchronous Pattern Overview"?
Posted
Updated 5-Apr-12 8:12am
v2

1 solution

I've done something similar in the past.
I noticed this:

  • Events can happen hundreds of times per second, but users don't read that fast.
  • A process can send thousands of updates to the progress form, but a progress bar won't be more than a few hundred pixels wide.
  • Drawing is expensive.

As a result, I use the following pattern:

  • The user interface is re-drawn on a timer, 2 or 3 times per second at most. This resulted in huge resource savings: on an old W98, single-core machine, processes started executing several times faster.
  • Worker thread notifications are handled by the UI thread by updating some member variables, without showing anything to the user.
  • The timer handler shows those member variables.
  • No locking. If an integer that runs, let's say, from 1 to 17342 has the value 123 (which is used to set the position of a progress bar) and a label shows "122 records handled", that's fine. Even if the scroll bar is off by one whole pixel.


I don't own the code, so I can't share it, but I hope the pattern is clear.
This worked for me, I hope it will work for you.
 
Share this answer
 
Comments
bobishkindaguy 5-Apr-12 6:17am    
Excellent idea, accumulate data as often as it changes, but only allow the data to be displayed when the timer fires 2 or 3 times per second.
But the question still remains... my import classes are raising "report progress" events - they are handled by the BackgroundWorker (or rather, the import classes are issuing the bw.ReportProgress command, which in turn appears in the UI thread as an event "ProgressChanged"), but what happens if the UI thread has started the process? Where do those events get handled then?
Pablo Aliskevicius 5-Apr-12 7:18am    
From your description, I assume that your UI is some kind of wizard, with the 'progress screen' near the end, and a BackgroundWorker in the outer frame, and that the 'manual update' runs synchronously in the UI thread.
I'd suggest putting another background worker instance in the form where the 'manual update' runs, and run also the 'manual update' in a background thread - of course, only if I guessed right about your architecture.
Hope this helps,
bobishkindaguy 5-Apr-12 14:09pm    
Pablo, you are a genius. You pegged my app exactly. Even as I was writing my original question, your suggestion occurred to me as well, but you know, when you're on a path that you have invested in, you are reluctant to abandon it. You think, "but it could still be useful as the app evolves, so I'm not ready to give up on it yet". I develop alone, so occasionally it is good to have peer opinion, and I thank you very much for providing that. Of course, your suggestion is the right path, and I will now implement that. I'm sure that "simplification" will result.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900