private void button1_Click(object sender, EventArgs e)
{
backworker.RunWorkerAsync();
}
private void backworker_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 10000; i++)
{
label1.Text = i.ToString();
DateTime d2 = DateTime.Now;
label2.Text = d2.ToString();
}
}
Friends i am working on comparing times between when the task of backworker finished, like how much time it tooks to finish the loop task
but when i do it, i tried to put the Comparasion after loop but it tells me error because d2 not declared
so how can i solve that to compare and get the exact time that the loop took to finish the task of printing numbers
I think this could work:
DateTime d2 = DateTime.Now;
for (int i = 0; i < 10000; i++)
{
...
}
label2.Text = (DateTime.Now - d2).ToString();
A good way to measure times is to use System.Diagnostics.Stopwatch:
var sw = Stopwatch.StartNew();
for (int i = 0; i < 10000; i++)
{
...
}
label2.Text = sw.Elapsed.ToString();
Your DateTime is declared withing the loop and therefore its scope is limited to the loop body. You must declare it before, i.e., outside, the loop to make it available after the loop. But it is better to use a Stopwatch for this purpose.
Another problem is that you are attempting to access a Control (a Label) from another thread than the UI thread. You are not allowed to do this.
Fortunately the BackgroundWorker Class can "Talk" to the UI thread through the ProgressChanged event.
Setup the BackgroundWorker with:
private void button1_Click(object sender, EventArgs e)
{
backworker.WorkerReportsProgress = true;
backworker.RunWorkerAsync();
}
Then declare another event handler which will automatically be called in the UI-thread:
private void Backworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
label1.Text = e.ProgressPercentage.ToString();
var ts = (TimeSpan)e.UserState;
label2.Text = ts.ToString(#"ss\.ff");
}
Now, change you worker to
private void Backworker_DoWork(object sender, DoWorkEventArgs e)
{
Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();
for (int i = 0; i < 100; i++) {
Thread.Sleep(100);
backworker.ReportProgress(i, stopWatch.Elapsed);
}
stopWatch.Stop();
backworker.ReportProgress(100, stopWatch.Elapsed);
}
Note that I have introduced a Thread.Sleep(100); and have diminished the number of loops by 100. This is because otherwise the UI cannot display the progress that fast. In a real scenario you would replace Thread.Sleep by some useful work.
Related
I'm populating datagridview with random numbers from a given range, but when I generate a big amount of numbers - my program hangs while generating them. That could last more that a minute (depending on the amount). I know that I can show a progress using ProgressBar. I've tried to use it, but I haven't got anythin. Any examples of using it?
Here is my code:
private void button1_Click(object sender, EventArgs e)
{
if (dataGridView1.RowCount > 0) {
dataGridView1.Rows.Clear();
dataGridView1.Refresh();
}
N = int.Parse(textBox1.Text);
range_min = int.Parse(textBox2.Text);
range_max = int.Parse(textBox3.Text);
numbers = new int[N];
if (range_max < range_min) MessageBox.Show("Some alert text");
else if (range_max == range_min) MessageBox.Show("Some alert text");
else
{
dataGridView1.RowCount = N;
for (int i = 0; i < N; i++)
{
numbers[i] = (int)(Math.Round((range_max - range_min) * rndm.NextDouble() + range_min));
dataGridView1[0, i].Value = numbers[i];
}
}
}
Your program "hangs" while adding data to the UI because you are doing all the work in the UI thread effectively blocking the thread until your loop is done. So you need to handle this heavy duty work in a seperate thread. But you can only change the UI from the UI/main thread so something like this would throw an exception:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
for (int i = 0; i < 4000; i++)
{
dataGridView1.Rows.Add(i.ToString());//throws an exception
}
}).Start();
}
The solution for this is to create a method which you can invoke the main thread to execute like so:
private void button1_Click(object sender, EventArgs e)
{
new Thread(() =>
{
for (int i = 0; i < 10000; i++)
{
this.AddRow(i);
}
}).Start();
}
public void AddRow(int value)
{
if (this.InvokeRequired)
this.Invoke(new MethodInvoker(() => this.AddRow(value)));
else
dataGridView1.Rows.Add(value.ToString());//do your ui update (add row, update progress bar etc..)
}
In my program I'm trying to see how long it will take to calculate an amount of prime numbers
double time;
private void button1_Click(object sender, EventArgs e)
{
bool isPrime = true;
timer1.Start();
for (int i = 2; i <= 200000; i++)
{
for (int j = 2; j <= 200000; j++)
{
if (i != j && i % j == 0)
{
isPrime = false;
break;
}
}
if (isPrime)
{
//listBox1.Items.Add(i);
}
isPrime = true;
}
timer1.Stop();
MessageBox.Show(time.ToString() + "ms");
}
private void timer1_Tick(object sender, EventArgs e)
{
time += 0.001; ;
}
However when the program finishes calculating the messagebox displays time as zero.
I tested the program using the stopwatch class and that works, however I was wondering is there any way to use a timer instead?
Thank you!
If this is purely academic:
myTimer.Interval = 1;
myTimer.Elapsed += IncrementEvent;
You could do the above to fire an event on each increment.
Causing the event to be fired every ~1ms.
public int incrementCounter {get; set; }
public void IncrementEvent()
{
incrementCounter++;
}
I also believe you'd need to wire this up for multi-threading. (I can't remember the last time I used a timer, and never in a tight loop where locking would be a concern.
and then obviously use that value in your message box:
MessageBox.Show(incrementCounter.ToString() + "ms");
However, I'd argue to never use anything like this in production.
You don't have much precision. 1ms is the smallest unit of time.
The timer isn't very reliable. Even running it every few seconds, you'll see milliseconds of the time executed vary slightly.
A timer is meant to schedule events to happen once t(ime) has ellapsed. There are much better tools for measuring how long an event takes.
I work on console version of link tester.
I start the function on multiple threads but I can't cancel them by key pressing.
Have an idea how I can do that ?
try
{
Thread[] tr = new Thread[Variables.Threads];
int i = 0;
while (i < Variables.Threads && Variables.running)
{
tr[i] = new Thread(new ThreadStart(Program.Runner));
i++;
}
//Start each thread
foreach (Thread x in tr)
{
x.Start();
}
//Console.ReadKey();
Task.Factory.StartNew(() =>
{
while (Colorful.Console.ReadKey().Key != ConsoleKey.Escape);
Variables.running = false;
foreach (Thread x in tr)
{
x.Abort();
}
Program.Menu();
});
}
catch (Exception)
{
}
EDIT : When my threads are a near of end, all of my app don't move after
The console is a not a good environment to start learning multitasking, much less multithreading. My advise to learning Multitasking - and especially Multithreading - is the BackgroundWorker in a WindowsForms applciation. The event queue does the main thing of keeping your application alive, without blocking I/O. And while the BackgroundWorker is horribly dated and should be removed from production code, it is pretty good training wheels.
Also your current code is swallowing exceptions, including fatal ones. This is a cardinal sin of exception handling. Do not do that.
At the end of the day, you can only do cancelation checking and progress reporting between distinct lines of code. In this BGW example, I had the advantage that I had to write all the loops anyway - so deep cancelation checking and progress reporting was in the cards. But if you use "somebody elses code", chances are high you have to wait for one function call to return before you can report and check.
#region Primenumbers
private void btnPrimStart_Click(object sender, EventArgs e)
{
if (!bgwPrim.IsBusy)
{
//Prepare ProgressBar and Textbox
int temp = (int)nudPrim.Value;
pgbPrim.Maximum = temp;
tbPrim.Text = "";
//Start processing
bgwPrim.RunWorkerAsync(temp);
}
}
private void btnPrimCancel_Click(object sender, EventArgs e)
{
if (bgwPrim.IsBusy)
{
bgwPrim.CancelAsync();
}
}
private void bgwPrim_DoWork(object sender, DoWorkEventArgs e)
{
int highestToCheck = (int)e.Argument;
//Get a reference to the BackgroundWorker running this code
//for Progress Updates and Cancelation checking
BackgroundWorker thisWorker = (BackgroundWorker)sender;
//Create the list that stores the results and is returned by DoWork
List<int> Primes = new List<int>();
//Check all uneven numbers between 1 and whatever the user choose as upper limit
for(int PrimeCandidate=1; PrimeCandidate < highestToCheck; PrimeCandidate+=2)
{
//Report progress
thisWorker.ReportProgress(PrimeCandidate);
bool isNoPrime = false;
//Check if the Cancelation was requested during the last loop
if (thisWorker.CancellationPending)
{
//Tell the Backgroundworker you are canceling and exit the for-loop
e.Cancel = true;
break;
}
//Determin if this is a Prime Number
for (int j = 3; j < PrimeCandidate && !isNoPrime; j += 2)
{
if (PrimeCandidate % j == 0)
isNoPrime = true;
}
if (!isNoPrime)
Primes.Add(PrimeCandidate);
}
//Tell the progress bar you are finished
thisWorker.ReportProgress(highestToCheck);
//Save Return Value
e.Result = Primes.ToArray();
}
private void bgwPrim_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
pgbPrim.Value = e.ProgressPercentage;
}
private void bgwPrim_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
pgbPrim.Value = pgbPrim.Maximum;
this.Refresh();
if (!e.Cancelled && e.Error == null)
{
//Show the Result
int[] Primes = (int[])e.Result;
StringBuilder sbOutput = new StringBuilder();
foreach (int Prim in Primes)
{
sbOutput.Append(Prim.ToString() + Environment.NewLine);
}
tbPrim.Text = sbOutput.ToString();
}
else
{
tbPrim.Text = "Operation canceled by user or Exception";
}
}
#endregion
However in your case, Multithreading seems unesseary. Multitasking without Threads would propably be better. Threads only help if you got a CPU bound task. And "checking links" sounds like a Network bound task. Threads have some extra headaches that you are better off avoiding.
First of all, My code is written in Windows Form Application - C#.
I need to execute a method (which is very modular and it's runtime is depends on how much physical memory you have used in your system), and while this method is running, I want to present to the user a progressbar. I don't know how to sync the progressbar, with the function's runtime.
EDIT: HERE IS MY CODE:
public SystemProp()
{
// Getting information about the volumes in the system.
this.volumes = getVolumes();
for (int i = 0; i <volumes.Length; i++)
{
// Create a txt file for each volume.
if (!System.IO.File.Exists(dirPath + volumes[i].Name.Remove(1) + #".txt"))
{
using (FileStream fs = File.Create(dirPath + volumes[i].Name.Remove(1) + #".txt"))
{
}
}
// Treescan function for each Volume.
TreeScan(volumes[i].Name);
}
}
private bool isSafe()
{ return true; }
private DriveInfo[] getVolumes()
{
DriveInfo[] drives = DriveInfo.GetDrives();
return drives;
}
private void TreeScan(string sDir)
{
try
{
foreach (string f in Directory.GetFiles(sDir))
{
using (FileStream aFile = new FileStream(dirPath + sDir.Remove(1) + #".txt", FileMode.Append, FileAccess.Write))
using (StreamWriter sw = new StreamWriter(aFile)) { sw.WriteLine(f); }
}
foreach (string d in Directory.GetDirectories(sDir))
{
TreeScan(d);
}
}
catch (Exception)
{ }
}
The function is the treescan.
I would appriciate any kind of help,
Thank You Very Much!!
You should calculate progress and set ProgressBar.Value inside the method.
For example you have a for loop from 1 to 100.
for (int i = 0; i < 100; i ++)
{
//...
progressBar.Value = i;
}
You can also set a maximum value of progress using Maximum property.
So for a for loop from 1 to 10 you can set Maximum to 10 and don't calculate a progress.
progressBar.Maximum = 10;
for (int i = 0; i < 10; i ++)
{
//...
progressBar.Value = i;
}
If you can't split you method in different stages where you can change progress value, you can create a timer that ticks every one second and change the progress value in Tick event handler.
In order to set progress value based on runtime you can use Stopwatch.
Timer and Stopwatch should be started in the beginning of the method.
Timer timer = new Timer();
Stopwatch stopwatch = new Stopwatch();
void Method()
{
timer.Start();
stopwatch.Start();
//...
}
private void Timer_Tick(object sender, EventArgs e)
{
var progress = CalculateProgress (); // calculate progress
progressBar.Value = progress;
// or
progressBar.Value = stopwatch.Elapsed.Seconds;
}
well i am new to C#, and implementing a code, in which i have two buttons, with one acting as starting of data acquisition and storing it in a csv file and other button to stop it.
well codes for all these are as follows:
//button for start DAQ
private void stdaq_Click(object sender, EventArgs e)
{
stopped = false;
process();
}
//button for stoping DAQ
private void spdaq_Click(object sender, EventArgs e)
{
stopped = true;
}
// process function
private process()
{
int iAvail = 0;
int iRead = 0;
string filename = #textBox3.Text;// taking csv file name from user
// jit:
//a function calculating the total number of values and storing it in iAvail
int[] iRawData = new Int32[iAvail];
double[] dScaledData = new Double[iAvail];
//a function transferring the data from buffer and storing it in dscaledData array
List<double> data = new List<double>();
for (int i = 0; i < iAvail; i++)
{
data.Add(dScaledData[i]);
}
Task myFirstTask = Task.Factory.StartNew(()
=>
{
while (stopped == false)
{
Write(data.ToArray(), filename);
// goto jit;
}
});
}
// csv creater and data writer
public static void Write(double[] data, string outputPath)
{
StringBuilder sb = new StringBuilder();
for (int i = 0; i < data.GetLength(0); i++)
{
if (stopped) break;
sb.AppendLine(string.Join(",", data[i]));
}
if (File.Exists(outputPath))
{
File.AppendAllText(outputPath, sb.ToString());
}
else
{
File.WriteAllText(outputPath, sb.ToString());
}
}
this is what i am implementing, and the problem with this code is that when the data is first transferred and written to the file, then again the same data is written again and again irrespective of new data and i tried implementing that Goto statement(can be seen in comments) but it is giving error - " Control cannot leave the body of an anonymous method or lambda expression ", and if i don't use the While loop the data is not written at all.
So i want to call my process function and to transfer data to csv starting on press of a start button, take fresh data everytime and write it to csv or can say call the process method again from it's start point and to stop it on click of the stop button, but i am unable to do it irrespective of various tries with different loops and some threading functions also.
please help with this.
Assuming you only need to Write once, you should remove this or change it from while to if:
while (stopped == false)
The loop will cause Write to be called infinitely until stopped becomes true.
Also, you might want to change Write to return rather than break if stopped is true, so that you don't write anything if you are supposed to be stopping:
if (stopped) break;
to
if (stopped) return;
If you want to generate data again and really do want to loop forever, just move that code into the loop:
Task myFirstTask = Task.Factory.StartNew(()
=>
{
while (stopped == false)
{
List<double> data = new List<double>();
// TODO: Generate data here - move all relevant code here
Write(data.ToArray(), filename);
}
});
I think this is a job for the BackgroundWorker.
This code will start you up:
public partial class Form1 : Form
{
int loopCounter = 0; // variable just used for illustration
private static BackgroundWorker bw = new BackgroundWorker(); // The worker object
// This function does your task
public void doSomeStuff(object sender, DoWorkEventArgs e)
{
for (int i = 0; i < 1000; i++)
{
loopCounter = i; // Pass the loop count to this variable just to report later how far the loop was when the worker got cancelled.
Thread.Sleep(100); // Slow down the loop
// During your loop check if the user wants to cancel
if (bw.CancellationPending)
{
e.Cancel = true;
return; // quit loop
}
}
}
// This button starts your task when pressed
private void button1_Click(object sender, EventArgs e)
{
bw.WorkerSupportsCancellation = true; // Set the worker to support cancellation
bw.DoWork += doSomeStuff; // initialize the event
if (!bw.IsBusy) // Only proceed to start the worker if it is not already running.
{
bw.RunWorkerAsync(); // Start the worker
}
}
// This button stops your task when pressed
private void button2_Click(object sender, EventArgs e)
{
// Request cancellation
bw.CancelAsync();
textBox1.Text = "The bw was cancelled when 'loopCounter' was at: " + loopCounter.ToString();
}
}