Solved

backgroundWorker.. DoWork and ProgressChanged

Posted on 2006-12-01
10
4,521 Views
Last Modified: 2008-01-09
Hello,

I using a backgroundWorker to execute a time consuming operation and print the result as they come an a listView. I'm basically doing something similar to the following:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
            while (condition)
            {
                if (backgroundWorker1.CancellationPending)
                {
                    e.Cancel = true;
                }
                else
                {
                    // code... bla bla bla
                   
                    globalItemName = someVar;

                    Console.WriteLine("In DoWork");
                   
                    backgroundWorker1.ReportProgress(1);
                }
            }
}

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            Console.WriteLine("In ProgressChanged");

            listView1.Items.Add(globalItemName);

            progressBar1.Value = e.ProgressPercentage;
        }

It's not working, all the 'In DoWork' is printed in the output window first then 'In ProgressChanged' is printed after.

What's wrong?

Thanks.
0
Comment
Question by:i950
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 6
  • 4
10 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 18058891
>> 'In DoWork' is printed in the output window first then 'In ProgressChanged' is printed after.

This is exactly what is written in your code:

Console.WriteLine("In DoWork");
backgroundWorker1.ReportProgress(1);          // In ProgressChanged

What is the problem exactly?
0
 

Author Comment

by:i950
ID: 18059629
Hi Alex,

I'm getting:

Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");

Not:

Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 18059720
I made small test and reproduced this behavour. This means, BackgroundWorker.ReportProgress invokes ProgressChanged event asynchronously. Line
backgroundWorker1.ReportProgress(1);
is executed, and worker thread code continues immediately, without waiting for ProgressChanged function. Computer makes thread switch with some time interval. In your case, backgroundWorker1_DoWork function is executed in one time interval scheduled to this thread. When thread exits, main thread is activated and executes all ProgressChanged events from event queue.
Make small change:
backgroundWorker1.ReportProgress(1);
System.Threading.Thread.Sleep(0);

Now you can see expected result. Sleep(0) causes thread context switch, and ProgressChanged is called immediately.
However, Sleep(0) is added only for test, remove it. Asynchronous behavior of BackgroundWorker is absolutely OK, this is the way multithreading must work.
0
Webinar: Aligning, Automating, Winning

Join Dan Russo, Senior Manager of Operations Intelligence, for an in-depth discussion on how Dealertrack, leading provider of integrated digital solutions for the automotive industry, transformed their DevOps processes to increase collaboration and move with greater velocity.

 
LVL 48

Expert Comment

by:AlexFM
ID: 18059739
BTW, you can add more code to backgroundWorker1_DoWork to make it more time consuming. This this case output will look like this (without Sleep):

Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In DoWork");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");
Console.WriteLine("In ProgressChanged");

Every thread executes its work in time interval given to this thread by operating system. This is exactly what we expect from parellel asynchronous execution.
0
 

Author Comment

by:i950
ID: 18059758
Ahaaaa... it's clear now.

So my whole idea to update the UI in ProgressChanged not going to work as expected. What I'm doing is declaring a global variable for the listView item text and I assigning this variable in DoWork then I call ReportProgress and in ReportProgress I use the global variable to get the list view item name, which may or may not be changed!!!!!

What's the correct way to update the UI in this case?
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 18059780
Correct way is using BackgroundWorker.ReportProgress Method (Int32, Object) overload. It allows to add instance of any class or primitive type (like string) as parameter.

backgroundWorker1.ReportProgress(1, someVar);


        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
               string s = (string)e.UserState;
               ...
        }
0
 

Author Comment

by:i950
ID: 18059806
It worked! Thanks!

Just one more thing...

How can I pass several variables (integers and strings) to ReportProgress?
0
 
LVL 48

Expert Comment

by:AlexFM
ID: 18059870
You need to define your own class which contains all these variables, and pass instance of this class as parameter.

BackgroundWorkerData d = new BackgroundWorkerData();   // your class
d.name = somevar;
d.number1 = ...;      // fill with any data

backgroundWorker1.ReportProgress(1, d);

        private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
               BackgroundWorkerData d = (BackgroundWorkerData)e.UserState;
               // read data here
               ...
        }
0
 

Author Comment

by:i950
ID: 18060075
Is there any performance overhead when initiating many BackgroundWorkerData here:

BackgroundWorkerData d = new BackgroundWorkerData();   // your class
d.name = somevar;
d.number1 = ...;      // fill with any data

I'll be using it in a loop that may be repeated several thousand times.
0
 
LVL 48

Accepted Solution

by:
AlexFM earned 500 total points
ID: 18060475
You must create new instance for every call. Maybe this is bit slower than using the same instance, but one instance doesn't work properly, as you see. Don't care about creating many instances, .NET Garbage Collector is very effective.
0

Featured Post

Salesforce Made Easy to Use

On-screen guidance at the moment of need enables you & your employees to focus on the core, you can now boost your adoption rates swiftly and simply with one easy tool.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Welcome my friends to the second instalment and follow-up to our Minify and Concatenate Your Scripts and Stylesheets (http://www.experts-exchange.com/Programming/Languages/.NET/ASP.NET/A_4334-Minify-and-Concatenate-Your-Scripts-and-Stylesheets.html)…
Today I had a very interesting conundrum that had to get solved quickly. Needless to say, it wasn't resolved quickly because when we needed it we were very rushed, but as soon as the conference call was over and I took a step back I saw the correct …
The Email Laundry PDF encryption service allows companies to send confidential encrypted  emails to anybody. The PDF document can also contain attachments that are embedded in the encrypted PDF. The password is randomly generated by The Email Laundr…
Finds all prime numbers in a range requested and places them in a public primes() array. I've demostrated a template size of 30 (2 * 3 * 5) but larger templates can be built such 210  (2 * 3 * 5 * 7) or 2310  (2 * 3 * 5 * 7 * 11). The larger templa…

751 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question