BackgroundWorker and foreach loop - c#

I have to process a loop with backgroundworkers.
Before I start a new loop iteration I need to wait until the provious backgroundworker has finished.
A while loop inside my foreach loop with isbusy flag doesn's seem like a good idea to me.
How should I design this loop so it waits for the bg-worker to end before iterating the loop
public void AutoConnect()
{
string[] HardwareList = new string[] { "d1", "d4", "ds1_2", "ds4_2" };
foreach (string HW in HardwareList)
{
if (backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync(HW);
// Wait here until backgroundWorker1 finished
}
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
string FileName = e.Argument as string;
try
{
if ((worker.CancellationPending == true))
{
e.Cancel = true;
}
else
{
// Time consuming operation
ParseFile(Filename);
}
}
catch { }
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString() + " lines";
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled == true)
{
//this.tbProgress.Text = "Canceled!";
}
else if(!(e.Error == null))
{
//this.tbProgress.Text = ("Error: " + e.Error.Message);
}
else
{
label1.text = "Done!";
}
}

You're using the backgroundworker wrong. Pass the entire list to the background worker and run the foreach loop there.
public void AutoConnect()
{
string[] HardwareList = new string[] { "d1", "d4", "ds1_2", "ds4_2" };
backgroundWorker1.RunWorkerAsync(HardwareList);
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
string[] FileNames = e.Argument as string[];
int i = 0;
foreach (string FileName in FileNames)
{
ParseFile(FileName);
worker.ReportProgress(++i);
if (worker.CancellationPending)
{
e.Cancel = true;
break;
}
}
}

The basic idea would be to move the foreach into the DoWork method. That could make use of cancellation (it does not seem very effective now).
string[] HardwareList = new string[] { "d1", "d4", "ds1_2", "ds4_2" };
backgroundWorker1.RunWorkerAsync(HardwareList);
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
string[] HardwareList = e.Argument as string[];
foreach (string HW in HardwareList)
{
if (worker.CancellationPending) ...
....
}
}

Related

how to return from the background worker to web UI in asp.net

background worker in asp.net does not write the content to the web UI when background worker completed. please tell me the reasons why. and how to recover.???
static BackgroundWorker bwProcess;
[WebMethod()]
public static int GetProgress()
{
return Percentage;
}
background worker starts when click event happen
protected void btnClick_Click(object sender, EventArgs e)
{
bwProcess = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);
bwProcess.ProgressChanged += new ProgressChangedEventHandler(bwProcess_ProgressChanged);
bwProcess.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwProcess_RunWorkerCompleted);
bwProcess.RunWorkerAsync("AsyncWorker");
}
do work event for the backgroundworker
void bwProcess_DoWork(object sender, DoWorkEventArgs e)
{
bwProcess.ReportProgress(1);
for (int i = 0; i <= 100; i++)
{
if (bwProcess.CancellationPending)
{
e.Cancel = true;
return;
}
bwProcess.ReportProgress(i);
Thread.Sleep(20);
}
e.Result = "100 %";
}
this part is not working. it run nut there is no response
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Response.WriteFile("D:\\Samples.xlsx");
}
You've passed something to e.Result in DoWork, but you don't anything with it in RunWorkerCompleted.
void bwProcess_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
var text = e.Result as string;
Response.Write(text);
Response.WriteFile("D:\\Samples.xlsx");
}

Managing multiple backgroundWorkers

I'm developing a WPF application that a backgroundWorker to populate a listBox with file thumbnails and wanted to ask if the approach is ok? The user could interrupt the current background worker at any time.
private readonly List<BackgroundWorker> _backgroundWorkers = new List<BackgroundWorker>();
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
var bw = (BackgroundWorker)sender;
// Loop logic
if (bw.CancellationPending)
{
e.Cancel = true;
return;
}
bw.ReportProgress(0, fd);
}
}
private void RunWorker()
{
foreach (BackgroundWorker bw in _backgroundWorkers.Where(bw => bw.IsBusy))
{
bw.CancelAsync();
}
// App logic...
int i = _backgroundWorkers.Count() + 1;
var worker = new BackgroundWorker {WorkerReportsProgress = true, WorkerSupportsCancellation = true};
worker.DoWork += bw_DoWork;
worker.RunWorkerCompleted += bw_RunWorkerCompleted;
worker.ProgressChanged += bw_ProgressChanged;
worker.RunWorkerAsync(i);
_backgroundWorkers.Add(worker);
}
The aim is to create a new backgroundWorker and cancel existing busy workers
One backgroundworker should be enough to get all the file thumbnails and update the listbox:
public partial class Form1 : Form
{
private readonly ListBox _listBox;
public Form1()
{
InitializeComponent();
_listBox = new ListBox();
var backgroundWorker = new BackgroundWorker
{
WorkerReportsProgress = true ,
WorkerSupportsCancellation = true
};
backgroundWorker.DoWork += backgroundWorker_DoWork;
backgroundWorker.ProgressChanged += backgroundWorker_ProgressChanged;
backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
backgroundWorker.RunWorkerAsync( new List<object>() );
}
void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
var result = new List<object>();
var backgroundWorker = ( BackgroundWorker ) sender;
var enumerable = ( IEnumerable<object> ) e.Argument;
foreach ( var item in enumerable )
{
if ( backgroundWorker.CancellationPending )
{
e.Cancel = true;
return;
}
// Logic to get file thumbnail
result.Add( item );
backgroundWorker.ReportProgress( 0 , item );
}
e.Result = result;
}
void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
_listBox.Items.Add( e.UserState );
}
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ( e.Cancelled ) return;
if ( e.Error != null )
{
// Handle exception
return;
}
var listOfThumbnails = e.Result;
}
}

Background worker to copy directory

In my Windows Form Application, certain directories are copied on user request. Currently copying these directories is running on UI thread as a result, I am not able see any progress of the process.
Currently the copy function is trigger my following code.
private void button5_Click(object sender, EventArgs e)
{
string[] str = comboBox2.Text.Split(' ');
string username = str[2].Replace("[", "").Replace("]", "");
label3.Text = comboBox3.Text;
DialogResult result = MessageBox.Show("Do you want to copy " + comboBox3.Text + " mozilla profile for " + username + " to Selected Servers?", "Confirmation", MessageBoxButtons.YesNoCancel);
if (result == DialogResult.Yes)
{
if (myCheck.Checked == true)
{
string path = getPath(comboBox3.Text);
try
{
string source = path + "\\Appdata";
string dest = "\\\\192.168.1.40\\C$\\Users\\" + username + "\\AppData\\Local\\Mozilla";
Copy(#source, #dest);
}
}
}
}
Copy functions has following Code.
public void Copy(string sourceDirectory, string targetDirectory)
{
DirectoryInfo diSource = new DirectoryInfo(sourceDirectory);
DirectoryInfo diTarget = new DirectoryInfo(targetDirectory);
//Gets size of all files present in source folder.
GetSize(diSource, diTarget);
maxbytes = maxbytes / 1024;
progressBar1.Maximum = maxbytes;
CopyAll(diSource, diTarget);
}
public void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
fi.CopyTo(Path.Combine(target.ToString(), fi.Name), true);
total += (int)fi.Length;
copied += (int)fi.Length;
copied /= 1024;
progressBar1.Visible = true;
progressBar1.Step = copied;
progressBar1.PerformStep();
label14.Visible = true;
label14.Text = (total / 1048576).ToString() + "MB of " + (maxbytes / 1024).ToString() + "MB copied";
label14.Refresh();
}
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name);
CopyAll(diSourceSubDir, nextTargetSubDir);
}
}
public void GetSize(DirectoryInfo source, DirectoryInfo target)
{
if (Directory.Exists(target.FullName) == false)
{
Directory.CreateDirectory(target.FullName);
}
foreach (FileInfo fi in source.GetFiles())
{
maxbytes += (int)fi.Length;//Size of File
}
foreach (DirectoryInfo diSourceSubDir in source.GetDirectories())
{
DirectoryInfo nextTargetSubDir = target.CreateSubdirectory(diSourceSubDir.Name);
GetSize(diSourceSubDir, nextTargetSubDir);
}
}
My code is working perfectly but i am not able to see the progress and not able to see the update in Label.
Can somebody help me to run this copy in new thread so that I can see the progress in progress bar.
Here's a simple example to use background worker.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
Shown += new EventHandler(Form1_Shown);
// To report progress from the background worker we need to set this property
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);
}
void Form1_Shown(object sender, EventArgs e)
{
// Start the background worker
backgroundWorker1.RunWorkerAsync();
}
// On worker thread so do our thing!
void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
// Your background task goes here
for (int i = 0; i <= 100; i++)
{
// Report progress to 'UI' thread
backgroundWorker1.ReportProgress(i);
// Simulate long task
System.Threading.Thread.Sleep(100);
}
}
// Back on the 'UI' thread so we can update the progress bar
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// The progress percentage is a property of e
progressBar1.Value = e.ProgressPercentage;
}
}
You can do the CopyAll in Do_Work and Report progress to show the progress on UI in a progress bar.
Not going to wrap it up for you but I think the following might get you on the right track.
Given you have "standard" :) button1 and progressBar1 you get nice non-blocking UI update using e.g. Task and BeginInvoke().
private void button1_Click(object sender, EventArgs e)
{
progressBar1.Minimum = 0;
progressBar1.Maximum = 5;
Task.Factory.StartNew(() =>
{
for (int i = 0; i <= 5; i++)
{
int progress = i;
progressBar1.BeginInvoke((Action)(() =>
{
progressBar1.Value = progress;
}));
Thread.Sleep(250);
}
});
}
Try this pseudo code:
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork +=
new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged +=
new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
private void button5_Click(object sender, EventArgs e)
{
// rest of the code above this...
if (bw.IsBusy != true)
{
bw.RunWorkerAsync();
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
// rest of the code above this...
Copy(#source, #dest);
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Visible = true;
progressBar1.Step = copied;
progressBar1.PerformStep();
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
MessageBox.Show("Completed");
}

C# BackGroundWorker with ProgressBar Updates after process complete

I have the following in a button click event:
private void buttonSubmitAchChecks_Click(object sender, EventArgs e)
{
if (backgroundWorker1.IsBusy) return;
SubmittingAch(true);
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.WorkerSupportsCancellation = true;
label_Status.Text = "Submitting checks to ACH ....";
var qry = from ds in checkTrans.IndividualCheck
where ds.SubmitToACH &&
ds.Status == "Entered" &&
ds.CheckAmount > 0 &&
ds.SubmitToACH
select ds;
if (qry.Count() <= 0)
{
label_Status.Text = "Nothing to submit. Check the Check Amount, ACH, and Status fields.";
}
else
{
progressBar1.Maximum = qry.Count();
progressBar1.Minimum = 0;
progressBar1.Step = 1;
backgroundWorker1.RunWorkerAsync(qry);
}
}
My backgroundWorker1_DoWork:
private void backgroundWorker1_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
else
{
var qry = e.Argument as EnumerableRowCollection<CheckTrans.IndividualCheckRow>;
if (qry != null)
{
Thread.Sleep(4000);
//item.Status = ach.SubmitCheck(item);
var ach = new SubmitAchChecks();
foreach (var item in qry)
{
ach.SubmitCheck(item);
backgroundWorker1.ReportProgress(1);
Console.Write("backgroundWorker1_dowork=" + progressBar1.Value.ToString() + "\r\n");
}
}
}
}
My Cancel Button:
private void cancelAsyncButton_Click(object sender, EventArgs e)
{
if (backgroundWorker1.WorkerSupportsCancellation == true)
{
label_Status.Text = "Cancelling...";
backgroundWorker1.CancelAsync();
}
}
My backgroundWorker1_RunWorkerCompleted:
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled == true)
{
label_Status.Text = "Canceled!";
}
else if (e.Error != null)
{
label_Status.Text = "Error: " + e.Error.Message;
}
else
{
label_Status.Text = "Done!";
}
SubmittingAch(false);
}
My backgroundWorker1_ProgressChanged:
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value += 1;
Console.Write("progressbar1.value=" + progressBar1.Value.ToString() + "\r\n");
}
I get following output in my debug window when I processed 2 items:
backgroundWorker1_dowork=0
backgroundWorker1_dowork=0
progressbar1.value=1
progressbar1.value=2
The event is firing, but as you can see from the console.write, it's happening AFTER the thread finishes. I get the progressbar scrolling, but only once the dowork has completed.
What have I done wrong on this? I'd like it to update as each item is completed.
It's due to the way threads work. ProgressChange is invoked on the UI thread using BeginInvoke, and therefore on another thread. Meanwhile, the worker thread continues running. Since there is not much work to do, the BackgroundWorker finishes its work before BeginInvoke actually invokes the method, because thread switches don't happen every CPU operation. They happen after quite a few. To avoid this, manually call the method that increments the ProgressBar's value using this.Invoke().

Communicating with another thread

This is how I create a thread that does domething reps times:
protected virtual void RedButtonClicked(object sender, System.EventArgs e)
{
Nuker n = new Nuker(target, reps);
bombThread = new Thread(new ThreadStart(n.nuke));
bombThread.Start();
}
The thread class:
public class Nuker
{
private string target;
private int reps;
//...
public void nuke()
{
for(int i=0; i<reps; ++i)
{
ICBM.nuke(target);
Thread.Sleep(5500);
}
System.Console.WriteLine("Done.");
}
}
(I create a new class to store some variables since I can't pass these in ThreadStart().)
Now I would like to have a simple visualisation of the process, let's say printing the current repetition in a text field on a form. How would I use the i from the loop to do that?
In the simplest form you proved a callback in the Nuker class
public Nuker(string target, int reps, Action reportCallback){..}
In the loop you just call reportCallback(i);
Nuker n = new Nuker(target, reps, ReportMethod);
with
private void ReportMethod(int currentIdx)
{
if (InvokeRequired) // Invoke if UI update
...
}
But, probably you want to use the BackgroundWorker that has build in methods for reporting progress on the UI thread. Just check the examples on MSDN.
you can do it with a backgroundworker, it´s one of the easiest threads :-)
below i have post you a sample that i have createt to teach some friends the use of the backgroundworker ;-)
private BackgroundWorker bw = new BackgroundWorker();
public Form1()
{
InitializeComponent();
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public void buttonStart_Click(object sender, EventArgs e)
{
if (bw.IsBusy != true)
bw.RunWorkerAsync(12); //Start
}
public int Pils(int i)
{
Thread.Sleep(2000);
bw.ReportProgress(70, "In the middle of the work..");
Thread.Sleep(2000);
bw.ReportProgress(90, "Returning the result..");
Thread.Sleep(2000);
return (2 * i);
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
bw.ReportProgress(20, "Waiting for cancel..");
Thread.Sleep(2000);
if ((bw.CancellationPending == true))
e.Cancel = true;
else
{
bw.ReportProgress(50, "Starting process..");
e.Result = Pils((int)e.Argument);
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
bw.ReportProgress(100, "Work done..");
if ((e.Cancelled == true))
textBox1.Text = "Canceled!";
else if (e.Error != null)
textBox1.Text = ("Error: " + e.Error.Message);
else textBox1.Text = e.Result.ToString();
}
private void buttonCancel_Click(object sender, EventArgs e)
{
if (bw.WorkerSupportsCancellation == true)
bw.CancelAsync();
}
private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
listBox1.Items.Add((e.ProgressPercentage.ToString() + "%") + " - " + e.UserState as String);
}
url to the blogpost: link

Categories

Resources