The following is my background worker thread
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Thread t1 = new Thread(Thread1);
t1.Start();
Thread t2 = new Thread(Thread2);
t2.Start();
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
}
}
Thread1 code is as follows
static void Thread1()
{
int nofiles=0;
int returned = checkforfolderthread(1);
int startvalue = 0;
int stopvalue = 5000;
if (returned == 1)
{
nofiles = countfiles();
startvalue = startvalue + (nofiles - 1) * 1000;
stopvalue = stopvalue - startvalue;
}
repeat(startvalue, stopvalue,1,nofiles-1);
}
Function called from a thread is as follows
static void repeat(int ini, int fin, int threadno, int startadd)
{
int i, j;
for (j = ini; j < ini + fin; j += 1000)
{
StringBuilder sb = new StringBuilder();
for (i = j; i < j + 1000; i += 100)
{
WebClient wc = new WebClient();
string add = System.String.Format("http://www.colourlovers.com/api/colors/new?numResults=100&resultOffset={0}", i);
try
{
string tobeadded = wc.DownloadString(add);
sb.AppendLine();
sb.Append(tobeadded);
}
catch (Exception)
{
break;
}
}
string folderpath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string filename = System.String.Format("DownloadPalette\\Thread{0}\\color{1}.xml",threadno,startadd);
string location = Path.Combine(folderpath, filename);
File.WriteAllText(location, sb.ToString());
startadd = startadd + 1;
}
}
What I would want to do is continuously update a progressbar after each for i loop is completed.
But I cannot access the progressbar from this function running in the background thread.
Please Help me
You miss this method..
// 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;
}
According on this reference : BackgroundWorker and ProgressBar demo
You should use invoke, as described here: http://msdn.microsoft.com/en-us/library/ms171728(v=vs.80).aspx
First create a method to update the progressbar(create it on the main thread GUI):
private void UpdateBar()
{
//your code to update
}
Then create a delegate and pass you method to it(this code is also for main thread GUI):
private delegate void UpdateProgressBarDelegate();
private UpdateProgressBarDelegate UpdateProgressBarDelegate_Object;
UpdateProgressBarDelegate_Object = new UpdateProgressBarDelegate(this.UpdateBar);
Now update it from another thread like this:
progressbar.Invoke(UpdateProgressBarDelegate_Object);
Here we are calling the delegate object which will call UpdateBar method on GUI thread with a safe thread call.
If you need to update more than just the progressbar value you could call a method and checking if an invoke is required. An invoke is required if you want to access a UI object from a separate thread.
private void updateProgress(object sender, int count, int total)
{
if (base.InvokeRequired)
{
base.Invoke(new ProcessCountHandler(this.updateProgress), new object[] { sender, count, total });
}
else if (count <= this.progressBar1.Maximum)
{
this.progressBar1.Value = count;
this.CompletedCount.Text = count.ToString("N0") + " of " + total.ToString("N0");
}
}
Related
I am trying to sort an array of 5 random integers in a listbox to ascending order. While creating a new thread and calling it separately, the form becomes unresponsive when I select the 'sort' button.
public partial class Form1 : Form
{
const int iSize = 5;
int[] iArray = new int[iSize];
Random rnd = new Random();
public Form1()
{
InitializeComponent();
}
Thread th;
private void btnGenerate_Click(object sender, EventArgs e)
{
for (int i = 0; i < iArray.Length; i++)
{
iArray[i] = rnd.Next(0, 5);
lbxNumbers.Items.Add(iArray[i]);
}
}
public void threadMethod()
{
bubbleSort(iArray);
foreach(int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
private void btnSort_Click(object sender, EventArgs e)
{
th = new Thread(threadMethod);
lblStatusUpdate.Text = "Sorting...";
}
private void btnClear_Click(object sender, EventArgs e)
{
lbxNumbers.Items.Clear();
}
public static void bubbleSort(int [] iArray)
{
bool isSorted = false;
int lastUnsorted = iArray.Length - 1;
while(!isSorted)
{
for(int i = 0; i < iArray.Length-1; i++)
{
if(iArray[i] > iArray[i+1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
}
public static void swap(int [] iArray, int i, int j)
{
int tmp = iArray[i];
iArray[i] = iArray[j];
iArray[j] = tmp;
}
}
I am uncertain where the thread actually kicks in. The listbox can generate the array immediately and clear it, but sort makes it freeze. I am also unsure whether I need to use the background worker tool on this form.
To expound on Dour High Arch's comment.
EDIT
There are a couple of issues with your code. UI updates can only be done on the UI thread. You can accomplish this with the SychronizationContext or async/await handles it for you. Also, your bubblesort was missing an exit condition for the while loop, so it would run forever. The following demonstrates how this would work using async/await.
private async void btnSort_Click(object sender, EventArgs e)
{
lblStatusUpdate.Text = #"Sorting...";
await Run();
}
public async Task Run()
{
//This line runs asynchronously and keeps the UI responsive.
await bubbleSort(iArray);
//The remaining code runs on the UI thread
foreach (int item in iArray)
{
lbxNumbers.Items.Add(item);
}
}
public async Task bubbleSort(int[] iArray)
{
await Task.Run(() =>
{
bool isSorted = false;
while (!isSorted)
{
isSorted = true; //You were missing this to break out of the loop.
for (int i = 0; i < iArray.Length - 1; i++)
{
if (iArray[i] > iArray[i + 1])
{
swap(iArray, i, i + 1);
isSorted = false;
}
}
}
});
}
You can not act on UI, or Main thread within of another.
This line :
lbxNumbers.Items.Add(item);
which is invoked from another thread can cause a trouble.
The proper way of handling this scenario in WindowsForms would be using of
Control.Invoke method.
I'm creating a program that uses a TextBox and a ProgressBar and each time the text changes in the TextBox or the ProgressBar performs a step the GUI freezes and I can't do anything until the operation is done.
I know I need to run them on a different thread but I searched the entire web and didn't find a method that works for me.
This is an example when I write a file and I need to get the progress of it:
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
and this is an example when I change text:
this.richTextBox1.Text = "Installing patch : ";
Any help would be appreciated.
Use a BackgroundWorker, put your for loop inside of DoWork method:
backgroundWorker1.DoWork += doWork;
private void doWork(object sender, DoWorkEventArgs e)
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
}
When you want to execute your loop call backgroundWorker1.RunWorkerAsync() method.
Also you can use ProgressChanged event to update your progress bar:
backgroundWorker1.DoWork += doWork;
backgroundWorker1.ProgressChanged += progressChanged;
private void doWork(object sender, DoWorkEventArgs e)
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
var value = int.Parse(Math.Truncate(percentage).ToString());
backgroundWorker1.ReportProgress(value);
}
}
private void progressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
Update: If you want to pass arguments to DoWork method you can do it when you calling the backgroundWorker1.RunWorkerAsync() method like this:
backgroundWorker1.RunWorkerAsync(argument);
And you can access it in DoWork method with the e.Argument. if you want to pass more than one argument you can do the trick with Anonymous Types.For example:
var myArguments = new { Property1 = "some value", Property2 = myId, Property3 = myList };
backgroundWorker1.RunWorkerAsync(myArguments);
In DoWork method:
dynamic arguments = e.Argument;
var arg1 = arguments.Property1;
var arg2 = arguments.Property2;
// and so on...
You could use the Task class to create and run background threads. A word of caution! Any time you want to update something from a thread that is not the main thread, you must use the Dispatcher to run code on the main thread.
My example code:
System.Threading.Tasks.Task task = new System.Threading.Tasks.Task(() =>
{
for (int l = 0; l < newfile.Length; l++)
{
vfswriter.Write(newfile[l]);
double percentage = ((double)l / (double)newfile.Length * 100);
//This runs the code inside it on the main thread (required for any GUI actions
App.Current.Dispatcher.Invoke((Action) (() =>
{
this.progressBar1.Value = int.Parse(Math.Truncate(percentage).ToString());
}
}
});
task.Start();
I'm using a thread to run a calculation in the background of my program. I start the thread at the start of my program. If I press a button before the thread is finished it will open the statusBar and "openedStatus" is set to true.
This will show the threads current progress and after the thread has finished I would like to execute the last part of my code:
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
This part of the code will throw an exception though because you can't close the statusbar cross-thread.
Now the question is: How can I execute that last part of the code after the thread is finished?
private StatusBar sb = new StatusBar();
private void startVoorraadCalculationThread()
{
sb.setMaxProgress(data.getProducten().getProductenCopy().Count);
Thread thread = new Thread(new ThreadStart(this.run));
thread.Start();
while (!thread.IsAlive) ;
}
private void run()
{
for (int i = 0; i < data.getProducten().getProductenCopy().Count; i++ )
{
sb.setProgress(i);
sb.setStatus("Calculating Voorraad: " + (i+1) + "/" + data.getProducten().getProductenCopy().Count);
data.getProducten().getProductenCopy()[i].getTotaalVoorraad(data.getMaten());
}
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
calculationFinished = true;
}
Using a backgroundWorker fixed my problem:
private void startVoorraadCalculationThread()
{
sb.setMaxProgress(data.getProducten().getProductenCopy().Count);
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < data.getProducten().getProductenCopy().Count; i++)
{
sb.setProgress(i);
sb.setStatus("Calculating Voorraad: " + (i + 1) + "/" + data.getProducten().getProductenCopy().Count);
data.getProducten().getProductenCopy()[i].getTotaalVoorraad(data.getMaten());
}
}
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (openedStatus)
{
sb.Close();
validateBeforeSave();
}
calculationFinished = true;
}
I am trying my hardest to learn Cross/Multi Threading, but I am very confused on the concept.
I made a sample application which is suppose to display i on a label.text via a thread.
it's not working because I am trying to access a thread other than it was created on, I've researched a lot and I am still confused on Invoking, Delegation, etc...
Here is my code:
private void s1_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(new ThreadStart(Start1));
Thread1.Start();
}
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
displaytext("Working.........", i);
Thread.Sleep(100);
}
}
public void displaytext(string thetext, int number)
{
t1.Text = thetext + " " + number;
}
What is a good way to get this working ?
Any help is greatly appreciated.
I am learning this for the love of programming.
I am trying to access a thread other than it was created on
The actual error is accessing a Windows Forms control on a thread other than the one creating it.
The fix: use Invoke.
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
t1.Invoke(() => displaytext("Working.........", i));
Thread.Sleep(100);
}
}
You gotta Invoke the function through delegate to get it to work.
private void s1_Click(object sender, EventArgs e)
{
Thread Thread1 = new Thread(new ThreadStart(Start1));
Thread1.Start();
}
public void Start1()
{
for (int i = 0; i < 1000; i++)
{
if(t1.InvokeRequired)
{
t1.Invoke(new MethodInvoker( () => displaytext("Working.........", i)));
}
else
{
displaytext("Working........", i);
}
Thread.sleep(100);
}
}
public void displaytext(string thetext, int number)
{
t1.Text = thetext + " " + number;
}
This is the code of the DoWork event the ProgressChanged event and the RunWorkerCompleted event. The problem is that the progressBar is getting to 100% much faster before the function process operation is end. So when the progressBar is up to 100% I'm getting error exception since the progressBar can't be 101%
I need somehow to make that the progressBar will get progress according to the function process / progress. I guess that something is wrong with my calculation in the DoWork event.
In the Form1 top I added:
Int i;
In the constructor I did:
i = 0;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.WorkerReportsProgress = true;
backgroundWorker1.RunWorkerAsync();
And this is the events of the Backgroundworker:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
//int currentLength;
//int currentIndex;
//int lastIndex = 0;
string startTag = "T256=\"";
string endTag = "\"";
int startTagWidth = startTag.Length;
//int endTagWidth = endTag.Length;
int index = 0;
h = new StreamReader(#"d:\DeponiaSplit\testingdeponias_Translated.txt");
while ((line = h.ReadLine()) != null)
{
if (index > f.LastIndexOf(startTag))
{
break;
}
int startTagIndex = f.IndexOf(startTag, index);
int stringIndex = startTagIndex + startTagWidth;
index = stringIndex;
int endTagIndex = f.IndexOf(endTag, index);
int stringLength = endTagIndex - stringIndex;
if (stringLength != 0)
{
string test = f.Substring(stringIndex, stringLength);
f = f.Substring(0, stringIndex) + line + f.Substring(stringIndex + stringLength);
if (listBox1.InvokeRequired)
{
textBox1.Invoke(new MethodInvoker(delegate { textBox1.Text = line; }));
}
i = i + 1;
System.Threading.Thread.Sleep(500);
worker.ReportProgress((i * 1));
}
}
h.Close();
StreamWriter w = new StreamWriter(#"D:\New folder (24)\000004aa.xml");
w.AutoFlush = true;
w.Write(f);
w.Close();
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
//this.progressBar1.Text = (e.ProgressPercentage.ToString() + "%");
progressBar1.Value = e.ProgressPercentage;
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
this.progressBar1.Text = "Canceled!";
}
else if (!(e.Error == null))
{
this.progressBar1.Text = ("Error: " + e.Error.Message);
}
else
{
this.progressBar1.Text = "Done!";
}
}
Why doesn't it work correctly?
Working using FileStream
Now I wanted to add to the progressBar a label which will show % and the label will move according to the progressBar I don't want to disable the progressBar green color progress just to add % and show the numbers in percentages.
So in the ProgressChanged event I did :
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;
label3.Text = (e.ProgressPercentage.ToString() + "%");
}
But it doesn't work - the label13 doesn't move only the % changed to 1. I want to see something like 1%...2%...3%... and so on. How can I do it?
Why don't you use File.ReadAllLines, it returns an array of strings. You could report your progress as the percentage of the line you are processing to the total number of lines:
string[] lines = File.ReadAllLines(#"d:\DeponiaSplit\testingdeponias_Translated.txt");
// ...
worker.ReportProgress(i * 100 / lines.Length);
The "ReportProgress()" method is reporting "i" right after "i" has been incremented with the "i+1" statment. i is a variable that is incremented every time you have found a string that has matched your provided parameters. I think the problem here is that you are reporting an integer without context. Do you know how many lines total in the file contain a string that matches your parameters. I would suggest reporting (i/totalNumLinesMatchingParameters) * 100. This would report a number in terms of percentage. However, this does not include the action of writing the File. So you may want to scale the above differently if you intend to include the action of writing the File in your progress bar/progress bar label...