ReportProgress doesn't call progressChanged with tasks in c# - c#

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int currentProgress=-1;
while (currentProgress<length)
{
currentProgress=Worker.progress;
backgroundWorker1.ReportProgress(currentProgress);
Thread.Sleep(500);
length = Worker.UrlList.Count;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
int ix = e.ProgressPercentage;
progressBar1.Value = ix;
lblText.Text =ix+" %";
}
I wrote a program to download page sources by reading a file have about 1000 URLs. so I used Tasks to download pages async. here Worker.progress is the currently executed URL amount. though the debuger hits the backgroundWorker1.ReportProgress(currentProgress); it never enter to the backgroundWorker1_ProgressChanged.
private void StartButton_Click(object sender, EventArgs e)
{
t.makeUrlList(inputFile);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork += backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerAsync();
t.RunTasks();
Application.Exit();
}
background worker initializes when start button clicks...
here is where my tasks are created....
public void RunTasks()
{
if (numOfTasks > UrlList.Count)
numOfTasks=UrlList.Count-1;
Task[] t = new Task[numOfTasks];
int j = 0;
while ( j < UrlList.Count-1)
{
for (int i = 0; (i < t.Count())&&(j<UrlList.Count-1); i++)
{
try
{
if (t[i].IsCompleted || t[i].IsCanceled || t[i].IsFaulted)
{
t[i] = Task.Run(() => FindWIN(j));
j++;
progress = j;
}
}
catch (NullReferenceException ex)
{
t[i] = Task.Run(() => FindWIN(j));
j++;
progress = j;
}
}
}
}

If you want to BackgroundWorker supports updating progress information, the value of WorkerReportsProgress should be set to true . If this property is true , the user code can call ReportProgress for initiating event ProgressChanged .
Background worker initialization:-
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.DoWork+=backgroundWorker1_DoWork;
backgroundWorker1.ProgressChanged+=backgroundWorker1_ProgressChanged;
backgroundWorker1.RunWorkerAsync();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int currentProgress = -1;
decimal length=1000;
while (currentProgress < length)
{
currentProgress = Worker.progress;
backgroundWorker1.ReportProgress(currentProgress);
Thread.Sleep(500);
length = Worker.UrlList.Count;
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
int ix = e.ProgressPercentage;
progressBar1.Value = ix;
lblText.Text = ix + " %";
}

See the demo code below. This is mostly untested, and certainly isn't 'production standard', but it should give you a good start!
It uses a ConcurrentQueue to hold the list of URLs to be processed. This is threadsafe, and makes life a lot easier.
It has a configurable number of urls and tasks. It's best not to make 1000 tasks, but instead have a queue of work items, and a smaller pool of Tasks which 'pull items' off the queue until it's empty. This means you can performance test different numbers of Tasks and find the best value for your problem.
It uses Invoke when updating the progress bar - this avoids the cross-thread exception.
No BackgroundWorker - just TaskFactory and Task
public partial class Form1 : Form
{
private const int UrlCount = 1000;
private const int taskCount = 10;
private ConcurrentQueue<string> urlList;
private List<Task> taskList;
public Form1()
{
InitializeComponent();
}
private void ResetQueue()
{
// fake up a number of strings to process
urlList = new ConcurrentQueue<string>(Enumerable.Range(0, UrlCount)
.Select(i => "http://www." + Guid.NewGuid().ToString() + ".com"));
}
private void button1_Click(object sender, EventArgs e)
{
ResetQueue();
var taskFactory = new TaskFactory();
// start a bunch of tasks
taskList = Enumerable.Range(0, taskCount).Select(i => taskFactory.StartNew(() => ProcessUrl()))
.ToList();
}
void ProcessUrl()
{
string current;
// keep grabbing items till the queue is empty
while (urlList.TryDequeue(out current))
{
// run your code
FindWIN(current);
// invoke here to avoid cross thread issues
Invoke((Action)(() => UpdateProgress()));
}
}
void FindWIN(string url)
{
// your code here
// as a demo, sleep a sort-of-random time between 0 and 100 ms
Thread.Sleep(Math.Abs(url.GetHashCode()) % 100);
}
void UpdateProgress()
{
// work out what percentage of the queue is processed
progressBar1.Value = (int)(100 - ((double)urlList.Count * 100.0 / UrlCount));
}
}

You should set WorkerReportsProgress property of your worker to true on initialization stage.

Related

How to use ProgressBar in C#?

I want to use the ProgressBar and run it from 100% to 0% within 2 seconds. I have written the following function but it does not seem to run properly. Please help! Thank you!
private async void progessbar()
{
for (iValue = 100; iValue >= 0; iValue--)
{
pbTime.Value = iValue;
await Task.Delay(20);
}
}
If you want to animate your progress bar smoothly, you should use a storyboard:
private void AnimateProgressBar()
{
var storyboard = new Storyboard();
var animation = new DoubleAnimation { Duration = TimeSpan.FromSeconds(2), From = 100, To = 0, EnableDependentAnimation = true };
Storyboard.SetTarget(animation, this.ProgressBar);
Storyboard.SetTargetProperty(animation, "Value");
storyboard.Children.Add(animation);
storyboard.Begin();
}
You are changing toolbar multiple times in the same Windows event. Windows updates GUI later when it is idle. Thus you probably see your toolbar jump from 0 to 100% after 2 second wait.
Just add timer to your control, set it when ready to start and do incremental updates.
// In your designer:
this.timer.Enabled = false;
this.timer.Interval = 200;
this.timer.Tick += new System.EventHandler(this.timer_Tick);
// when starting the progress:
pbTime.Value = 0
this.timer.Enabled = true;
private void timer_Tick(object sender, EventArgs e)
{
if (pbTime.Value < 100)
pbTime.Value += 10;
}
I hope this helps
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, System.EventArgs e)
{
// Start the BackgroundWorker.
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
/*Your main code*/
for (int i = 1; i <= 100; i++)
{
// Wait 20 milliseconds.
Thread.Sleep(20);
// Report progress.
backgroundWorker1.ReportProgress(i);
}
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// Change the value of the ProgressBar to the BackgroundWorker progress.
progressBar1.Value = e.ProgressPercentage;
// Set the text.
this.Text = e.ProgressPercentage.ToString();
}
}
}

Async Behaviour

I have the following code to update the progress bar in async fashion and i notice
its async behaviour through the call to MessageBox.In this case it works perfectly
but when i give a sleep of 1s(1000) the MessageBox doesnot pops up and the the complete progress bar fills at once.
Kindly tell why this is happening.
private void button1_Click(object sender, EventArgs e)
{
Update_Async async = new Update_Async(Update_Async_method);
progressBar1.BeginInvoke(async,10);
MessageBox.Show("Updation In Progress");
}
public void Update_Async_method(int a)
{
this.progressBar1.Maximum = a;
for (int i = 1; i <= a; i++)
{
progressBar1.Value = a;
Thread.Sleep(10);
//Thread.Sleep(1000);
}
}
Try Update_Async.BeginInvoke(async, 10) instead if you want the delegate to run asynchrnously but, you'll have to cross thread checking on the update to the progress bar.
In response to your comment, very similar to what you are doing already,
void UpdatingFunction(int value)
{
if (this.progressBar.InvokeRequired)
{
this.progressBar.BeginInvoke(UpdatingFunction, value);
return;
}
// Invoke not required, work on progressbar.
}
This also explains what the Invoke methods on controls are for.
Delegate.BeginInvoke will run a method in a thread once and then dispose it. It is a poor choice if you want to repeatedly do some work in a thread and return intermediate results. If that is what you want, you should use BackgroundWorker. Highly abbreviated snippet:
BackgroundWorker bw;
YourFormConstructor()
{
...
bw = new BackgroundWorker();
bw.WorkerReportsProgress = true;
bw.DoWork += BackgroundCalculations;
bw.ProgressChanged += ShowBackgroundProgress;
}
private void button1_Click(object sender, EventArgs e)
{
bw.RunWorkerAsync(10);
}
void ShowBackgroundProgress(object sender, ProgressChangedEventArgs e)
{
this.progressBar.Value = e.ProgressPercentage;
}
static void BackgroundCalculations(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = sender as BackgroundWorker;
int max = (int)e.Argument;
for (int i = 0; i < max; i++)
{
bw.ReportProgress(i * 100 / max);
Thread.Sleep(10);
}
bw.ReportProgress(100);
}
}

Using a Progress bar on Form Load

I am trying to Design a WinForms control in C# which will get some data from a database while it's loading.
I want to use a progress bar to show the progress.
I tried this code (and also many others):
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Show();
progressBar1.Value = 10;
int n;
n = 50;//number of records in DB ,
double progress = 0;
double progstep = 25 / n;
for (int i = 1; i <= n; i++)
{
//getting
if (progress <= 100)
progressBar1.Value = (int)progress;
}
progressBar1.Value = 35;
n = 100;//number of records in DB for another data reading from DB ,
progress = 35;
progstep = 65 / n;
for (int i = 1; i <= n; i++)
{
//getting data from DB
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
progress += progress;
if (progress <= 100)
progressBar1.Value = (int)progress;
}
}
But, the problem is that the form will wait until data reading progress is completed, and I can see just a full progress bar and all data loaded.
What should I do to fix this?
Since this is winforms, i'd recommend using a BackgroundWorker.
Basic example:
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.ProgressChanged += new DoWorkEventHandler(bgWorker_ProgressChanged);
bgWorker.RunWorkerAsync(//pass in object to process)
Which would then kickoff:
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
//Do all of your work here
bgWorker.ReportProgress(); //percent done calculation
}
Then the Progress changed event would fire to update the UI safely:
private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}
Add a backgroundWorker1 to your form.
Then add a YourForm_Shown event
private void YourForm_Shown(object sender, EventArgs e)
{
backgroundWorker1.RunWorkerAsync();
}
Add on form's constructor after InitializeComponent()
backgroundWorker1.WorkerReportsProgress = true;
// This event will be raised on the worker thread when the worker starts.
backgroundWorker1.DoWork += new
DoWorkEventHandler(backgroundWorker1_DoWork);
// This event will be raised when we call ReportProgress.
backgroundWorker1.ProgressChanged += new
ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
And last add the voids of backgroundWorker1:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
And:
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= n; i++)
{
//getting data from DB.
dataGridView1.Rows.Add(....);
//Adding that data to a datagrid -- parametrs removed.
backgroundWorker1.ReportProgress(i);
// Simulate long task
}
}
This is simple mockup to show you how to work with background worker:
First in your OnLoad create background worker and attach 2 events to it:
var bw = new BackgroundWorker();
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(WorkCompleted);
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerAsync(data); // Assume data is list of numbers.
private void WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
// After work completed remove event handlers and dispose.
{
var bw = (BackgroundWorker)sender;
bw.RunWorkerCompleted -= WorkCompleted;
bw.DoWork -= DoWork; bw.Dispose();
}
private void DoWork(object sender, DoWorkEventArgs e)
{
var data = (List<int>)e.Argument;
foreach (var number in data)
{
if (progressBar1.InvokeRequired)
{
progressBar1.Invoke((MethodInvoker)delegate
{ this.ProcessNumber(number); });
}
else
{
ProcessNumber(number);
}
}
}
private void ProcessNumber(int i)
{
progressBar1.PerformStep();
//do something with i
}
Take a look at BackgroundWorker control. During form load invoke;
backgroundWorker.RunWorkerAsync();
and override event DoWork to do the dirty work (load data from database) and ProgressChanged to update progress bar. In the event body (lets say the event signature will be something like this):
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e) {
var worker = (BackgroundWorker)sender;
// time consuming operation
worker.ReportProgress(10, null);
// ... another stuff
}
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar.Value = e.ProgressPercentage;
}

How to track the completion of all the threads. C#

I need to freeze the main thread until the end recursion.
Recursion depth = count of threads.
sample code:
BackgroundWorker backgroundWorker1;
Random ran;
private void Form1_Load(object sender, EventArgs e)
{
method();
label1.Text = "Threads is finished";
}
private void method() // recursive method
{
Thread.Sleep(100);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync(); //Beginning new thread
}
private void backgroundWorker1_DoWork(object sender,
DoWorkEventArgs e)
{
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
method();
}
}
When the threads are completed, label1.Text must have the value "Threads is finished". How is this done?
Console application PoC which caches references to all created workers and uses numeric variable to check how many workers still in progress, when this value reaches 0 - application terminates. Please let me know in case of any questions.
class Program
{
private static IList<BackgroundWorker> workers;
private static Random ran;
private static int activeWorkersCount;
static void Main(string[] args)
{
workers = new List<BackgroundWorker>();
DoWork();
while (activeWorkersCount > 0)
{
Thread.Sleep(200);
}
Console.WriteLine("Waiting for all workers to finish...");
Console.ReadLine();
}
private static void DoWork() // recursive method
{
Thread.Sleep(100);
var newWorker = new BackgroundWorker();
newWorker.DoWork += BackgroundWorkerDoWork;
newWorker.RunWorkerCompleted += (o, e) =>
{
Console.WriteLine("[E] Worker finished");
Interlocked.Decrement(ref activeWorkersCount);
};
Interlocked.Increment(ref activeWorkersCount);
newWorker.RunWorkerAsync();
}
private static void BackgroundWorkerDoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("[S] Worker started");
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
DoWork();
}
}
}
When you decide it's time to stop recursion you can post a callback onto the GUI thread. Off the top of my head it'll be something like this:
if(ran.Next(1,5)!=1)
{
method();
}
else
{
Action action=()=>label1.Text = "Threads is finished";
this.BeginInvoke(action);
}
1) Create a method to update Label1:
private void WorkFinished()
{
if(Label1.InvokeRequired)
{
Label1.Invoke(WorkFinished);
}
else
{
Label1.Text = "Threads is finished";
}
}
2) Call WorkFinished() from backgroundWorker1_DoWork.
You can use CountDownEvent class:
Example:
using (CountDownEvent countdownEvent = new CountdownEvent(numberOfThreads))
{
for (int threadIndex= 0; i < numberOfThreads; threadIndex++)
ThreadPool.QueueUserWorkItem(
th =>
{
DoWork();
countdownEvent.Signal();
}, threadIndex);
countdownEvent.Wait();
}
Console.WriteLine("All threads complete");
I did it using a volatile integer number and it works well.
BackgroundWorker backgroundWorker1;
Random ran;
long runningThreads = 0;
public void Start()
{
method();
// Console.WriteLine("Threads is finished");
}
private void method() // recursive method
{
Interlocked.Increment(ref runningThreads);
Console.WriteLine("New thread started");
Thread.Sleep(100);
backgroundWorker1 = new BackgroundWorker();
backgroundWorker1.DoWork +=
new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerAsync(); //Beginning new thread
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
ran = new Random();
Thread.Sleep(ran.Next(500, 1000));
if (ran.Next(1, 5) != 1) // if = 1 then to stop recursion
{
method();
}
Finished();
}
private void Finished()
{
Interlocked.Decrement(ref runningThreads);
if (Interlocked.Read(ref runningThreads) == 0)
{
Console.WriteLine("Threads is finished");
}
}

Why does the BackgroundWorker in WPF need Thread.Sleep to update UI controls?

namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Window1 : Window
{
BackgroundWorker bgWorker;
Action<int> myProgressReporter;
public Window1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += bgWorker_Task;
bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;
// hook event to method
bgWorker.ProgressChanged += bgWorker_ReportProgress;
// hook the delegate to the method
myProgressReporter = updateProgress;
bgWorker.WorkerReportsProgress = true;
}
private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
object result;
result = e.Result;
MessageBox.Show(result.ToString());
progressBar1.Value = 0;
button1.IsEnabled = true;
}
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
//Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
}
private void updateProgress(int progressPercentage)
{
progressBar1.Value = progressPercentage;
}
private void bgWorker_Task(Object sender, DoWorkEventArgs e)
{
int total = 1000;
for (int i = 1; i <= total; i++)
{
if (bgWorker.WorkerReportsProgress)
{
int p = (int)(((float)i / (float)total) * 100);
bgWorker.ReportProgress(p);
}
Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception,
}
e.Result = "Completed";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if(!bgWorker.IsBusy)
bgWorker.RunWorkerAsync("This is a background process");
button1.IsEnabled = false;
}
}
}
Because in your (artificial) scenario you pump 1000 request-for-update to the main thread.
It gets no time to do an idle loop (necessary to do a screen update).
But (thanks to TerrorAustralis), you should start with merging your bgWorker_ReportProgress and myProgressReporter methods. You are now synchronizing twice, a possible cause of stackoverflow. Dispatching the UpdateProgress events is one of the main features of the Backgroundworker:
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
//System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
//disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
Posibility:
Dispatcher.BeginInvoke() is an Asynchronous operation. Since this is the case, you are able to try to hit it again before it completes its operation. To see if this is the problem, try Dispatcher.Invoke() which is Synchronous
As a possible workaround, if you just want to update the progress bar, the backgroundWorker ProgressChanged event could do this without the use of an explicit dispatcher.

Categories

Resources