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.
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..)
}
Perhaps I am over thinking this but I have a number of threads doing a rather lengthy process and I want to be able to cleanly abort them if necessary. This is primarily because I don't want to start new threads until the old ones are finished. Is there a way to tell via Event or other method that a thread is fully aborted? Or should I not worry about this? Currently I have code in my abort method as follows:
private void AbortProcessing()
{
if (!Processing) return;
StopFlag = true;
for (int x = 0; x < MaxThreads; x++)
{
try
{
BW[x].CancelAsync();
}
catch { }
//Processing = false;
}
bool aborted = false;
while (!aborted)
{
aborted = true;
for (int x = 0; x < MaxThreads; x++)
{
if (BW[x].IsBusy) aborted = false;
}
Thread.Sleep(100);
}
}
The IsBusy is always true, forever, after signalling abort so this doesn't work. Any suggestions?
try this:Simple Sample for this
Thread o;
private void button1_Click(object sender, EventArgs e)
{
ThreadStart starter = ThreaadFunction;
starter += () => {
//this is this event you want to do any thing after a threaad finished the job
};
o = new Thread(starter) {IsBackground =true };
o.Start();
}
public void ThreaadFunction()
{
Thread.Sleep(1000);
// what you do in thread
}
}
I'm trying to build a Windows Forms tool that runs queries asynchronously.
The app has a datagridview with 30 possible queries to run. The user checks the queries he wants to execute, say 10 queries, and hits a button.
The app has a variable called maxthreads = 3 (for the sake of discussion) that indicates how many threads can be used to async run the queries. The queries run on a production environment and we don't want to overload the system with too many threads running in the same time. Each query runs for an average of 30 sec. (some 5 min., others 2 sec.)
In the datagridview there is an image column containing an icon that depicts the status of each query (0- Available to be run, 1-Selected for running, 2- Running, 3- Successfully completed, -1 Error)
I need to be able to communicate with the UI every time a query starts and finishes. Once a query finishes, the results are being displayed in a datagridview contained in a Tabcontrol (one tab per query)
The approach: I was thinking to create a number of maxthread backgroundworkers and let them run the queries. As a backgroundworker finishes it communicates to the UI and is assigned to a new query and so on until all queries have been run.
I tried using an assignmentWorker that would dispatch the work to the background workers but don't know how to wait for all threads to finish. Once a bgw finishes it reports progress on the RunWorkerCompleted event to the assignmentWorker, but that one has already finished.
In the UI thread I call the assignment worker with all the queries that need to be run:
private void btnRunQueries_Click(object sender, EventArgs e)
{
if (AnyQueriesSelected())
{
tcResult.TabPages.Clear();
foreach (DataGridViewRow dgr in dgvQueries.Rows)
{
if (Convert.ToBoolean(dgr.Cells["chk"].Value))
{
Query q = new Query(dgr.Cells["ID"].Value.ToString(),
dgr.Cells["Name"].Value.ToString(),
dgr.Cells["FileName"].Value.ToString(),
dgr.Cells["ShortDescription"].Value.ToString(),
dgr.Cells["LongDescription"].Value.ToString(),
dgr.Cells["Level"].Value.ToString(),
dgr.Cells["Task"].Value.ToString(),
dgr.Cells["Importance"].Value.ToString(),
dgr.Cells["SkillSet"].Value.ToString(),
false,
new Dictionary<string, string>()
{ { "#ClntNb#", txtClntNum.Text }, { "#Staff#", "100300" } });
qryList.Add(q);
}
}
assignmentWorker.RunWorkerAsync(qryList);
}
else
{
MessageBox.Show("Please select at least one query.",
"Warning",
MessageBoxButtons.OK,
MessageBoxIcon.Information);
}
}
Here is the AssignmentWorker:
private void assignmentWorker_DoWork(object sender, DoWorkEventArgs e)
{
foreach (Query q in (List<Query>)e.Argument)
{
while (!q.Processed)
{
for (int threadNum = 0; threadNum < maxThreads; threadNum++)
{
if (!threadArray[threadNum].IsBusy)
{
threadArray[threadNum].RunWorkerAsync(q);
q.Processed = true;
assignmentWorker.ReportProgress(1, q);
break;
}
}
//If all threads are being used, sleep awhile before checking again
if (!q.Processed)
{
Thread.Sleep(500);
}
}
}
}
All bgw run the same event:
private void backgroundWorkerFiles_DoWork(object sender, DoWorkEventArgs e)
{
try
{
Query qry = (Query)e.Argument;
DataTable dtNew = DataAccess.RunQuery(qry).dtResult;
if (dsQryResults.Tables.Contains(dtNew.TableName))
{
dsQryResults.Tables.Remove(dtNew.TableName);
}
dsQryResults.Tables.Add(dtNew);
e.Result = qry;
}
catch (Exception ex)
{
}
}
Once the Query has returned and the DataTable has been added to the dataset:
private void backgroundWorkerFiles_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
try
{
if (e.Error != null)
{
assignmentWorker.ReportProgress(-1, e.Result);
}
else
{
assignmentWorker.ReportProgress(2, e.Result);
}
}
catch (Exception ex)
{
int o = 0;
}
}
The problem I have is that the assignment worker finishes before the bgw finish and the call to assignmentWorker.ReportProgress go to hell (excuse my French).
How can I wait for all the launched bgw to finish before finishing the assignment worker?
Thank you!
As noted in the comment above, you have overcomplicated your design. If you have a specific maximum number of tasks (queries) that should be executing concurrently, you can and should simply create that number of workers, and have them consume tasks from your queue (or list) of tasks until that queue is empty.
Lacking a good Minimal, Complete, and Verifiable code example that concisely and clearly illustrates your specific scenario, it's not feasible to provide code that would directly address your question. But, here's an example using a List<T> as your original code does, which will work as I describe above:
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace TestSO42101517WaitAsyncTasks
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
int maxTasks = 30,
maxActive = 3,
maxDelayMs = 1000,
currentDelay = -1;
List<TimeSpan> taskDelays = new List<TimeSpan>(maxTasks);
for (int i = 0; i < maxTasks; i++)
{
taskDelays.Add(TimeSpan.FromMilliseconds(random.Next(maxDelayMs)));
}
Task[] tasks = new Task[maxActive];
object o = new object();
for (int i = 0; i < maxActive; i++)
{
int workerIndex = i;
tasks[i] = Task.Run(() =>
{
DelayConsumer(ref currentDelay, taskDelays, o, workerIndex);
});
}
Console.WriteLine("Waiting for consumer tasks");
Task.WaitAll(tasks);
Console.WriteLine("All consumer tasks completed");
}
private static void DelayConsumer(ref int currentDelay, List<TimeSpan> taskDelays, object o, int workerIndex)
{
Console.WriteLine($"worker #{workerIndex} starting");
while (true)
{
TimeSpan delay;
int delayIndex;
lock (o)
{
delayIndex = ++currentDelay;
if (delayIndex < taskDelays.Count)
{
delay = taskDelays[delayIndex];
}
else
{
Console.WriteLine($"worker #{workerIndex} exiting");
return;
}
}
Console.WriteLine($"worker #{workerIndex} sleeping for {delay.TotalMilliseconds} ms, task #{delayIndex}");
System.Threading.Thread.Sleep(delay);
}
}
}
}
In your case, each worker would report progress to some global state. You don't show the ReportProgress handler for your "assignment" worker, so I can't say specifically what this would look like. But presumably it would involve passing either -1 or 2 to some method that knows what to do with those values (i.e. what would otherwise have been your ReportProgress handler).
Note that the code can simplified somewhat, particularly where the individual tasks are consumed, if you use an actual queue data structure for the tasks. That approach would look something like this:
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
namespace TestSO42101517WaitAsyncTasks
{
class Program
{
static void Main(string[] args)
{
Random random = new Random();
int maxTasks = 30,
maxActive = 3,
maxDelayMs = 1000,
currentDelay = -1;
ConcurrentQueue<TimeSpan> taskDelays = new ConcurrentQueue<TimeSpan>();
for (int i = 0; i < maxTasks; i++)
{
taskDelays.Enqueue(TimeSpan.FromMilliseconds(random.Next(maxDelayMs)));
}
Task[] tasks = new Task[maxActive];
for (int i = 0; i < maxActive; i++)
{
int workerIndex = i;
tasks[i] = Task.Run(() =>
{
DelayConsumer(ref currentDelay, taskDelays, workerIndex);
});
}
Console.WriteLine("Waiting for consumer tasks");
Task.WaitAll(tasks);
Console.WriteLine("All consumer tasks completed");
}
private static void DelayConsumer(ref int currentDelayIndex, ConcurrentQueue<TimeSpan> taskDelays, int workerIndex)
{
Console.WriteLine($"worker #{workerIndex} starting");
while (true)
{
TimeSpan delay;
if (!taskDelays.TryDequeue(out delay))
{
Console.WriteLine($"worker #{workerIndex} exiting");
return;
}
int delayIndex = System.Threading.Interlocked.Increment(ref currentDelayIndex);
Console.WriteLine($"worker #{workerIndex} sleeping for {delay.TotalMilliseconds} ms, task #{delayIndex}");
System.Threading.Thread.Sleep(delay);
}
}
}
}
I have been trying to work out why my background worker is 'finishing' its work when there is still a lot for it to do. I am actually in the process of refactoring the code for this app, so it did work in the past, but now I am unable to figure out what has gone wrong.
Specifically, the app should open Outlook and then perform a few checks. However, the background worker exits straight after Outlook is opened for no apparent reason (as you will se below there is still plenty of processing to be done).
This appears to be happening early on in the Start() method, directly after calling Process.Start() on Outlook.exe.
The code runs in this order:
calling the background worker - this was the user's choice from a radio set
....
else if (radioButton5.Checked == true)
{
textBox1.Text = "Please wait while your session restarts";
pageControl1.SelectedIndex = 10;
backgroundReset.RunWorkerAsync();
}
The do-work method
public void backgroundReset_DoWork(object sender, DoWorkEventArgs e)
{
backgroundReset.WorkerSupportsCancellation = true;
Session.Reset();
}
the reset session method starts by killing the current session ...
public static void Reset()
{
KillSession();
System.Threading.Thread.Sleep(5000);
Start();
// THE BACKGROUNDWORKER EXITS BEFORE HERE!
if (IsLoggedIn() == false)
{
return;
}
else
{
// Make sure Lync is open before finishing the process ...
var j = 0;
GetSession(Init.servers);
j = 0;
var checker = false;
checker = ProcessHandler.CheckRunning("lync.exe");
while (checker == false)
{
if (j == 100)
{
break;
}
Thread.Sleep(500);
checker = ProcessHandler.CheckRunning("lync.exe");
j++;
}
}
}
As you can see from the comment, the backgroundworder is calling RunWorkerCompleted way before the Reset() method has finished executing.
Below are the other methods called (kill, logoff, start):
KillSession logs the session of and then makes sure it is logged off
private static void KillSession()
{
if (sessionId != null)
{
LogOff();
for (int i = 0; i < 150; i++)
{
if (IsLoggedIn() == true)
{
Thread.Sleep(500);
}
else
{
break;
}
}
}
}
LogOff sends a Cmd command to log off the current session
public static void LogOff()
{
string strCmdIn = "/C LOGOFF " + sessionId + " /SERVER:" + serverName;
Cmd.Exec(strCmdIn);
}
Start() Simply opens Outlook, causing a Citrix session to also start. The app is definitely launching Outlook, but after that it doesn't reach either of the for statements - the BackgroundWorker just exits.
public static void Start()
{
Process.Start(appDataCitrix + "Outlook.exe");
for (int i = 0; i < 15; i++)
{
if (IsLoggedIn2() == false)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
if (IsLoggedIn2() == false)
{
Process.Start(appDataCitrix + "Outlook.exe");
for (int i = 0; i < 10; i++)
{
if (IsLoggedIn2() == false)
{
Thread.Sleep(1000);
}
else
{
break;
}
}
}
}
Does anyone have any idea what is going on here? It is driving me crazy!
Many thanks
Update
The RunWorkerCompleted Method:
As far as my understanding goes, this has no baring on when the process will finish.
public void backgroundReset_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Session.IsLoggedIn())
{
btnFailFinish.Visible = true;
label10.Text = Session.serverName;
pageControl1.SelectedIndex = 3;
}
else
{
pageControl1.SelectedIndex = 10;
pictureBox2.Visible = false;
textBox1.Text = "Double-click Outlook on your desktop to launch a new session.";
textBox15.Text = "Once you have done this please click Finish.";
pictureBox9.Visible = true;
}
}
This is probably because of an exception being thrown from within the start method.
You may either add a try / catch block all around this method and handle the error from within the catch, or check in the RunWorkerCompleted method if an exception occurred :
private void RunWorkerCompleted (object sender, RunWorkerCompletedEventArgs e)
{
if (e.Error != null)
{
// handle your exception here
}
}
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();
}
}