WinForm C# "new Process" in infinite loop causing application to crash - c#

I have a requirement for which I need my block of code to run infinitely (The exit is an interrupt based on button click).
In each iteration, I am creating a Process, starting it, manipulating the output and then disposing the Process.
void status()
{
do{
Process test1 = new Process();
test1.StartInfo.FileName = "doSomething"; // doSomething is executable
test1.StartInfo.UseShellExecute = false;
test1.StartInfo.CreateNoWindow = true;
test1.StartInfo.RedirectStandardOutput = true;
test1.StartInfo.RedirectStandardError = true;
test1.Start();
string output = test1.StandardOutput.ReadToEnd();
test1.WaitForExit();
if (Regex.IsMatch(output, "DEVICE_READY", RegexOptions.IgnoreCase))
{
pictureBox2.BackColor = Color.Green;
}
else
{
pictureBox2.BackColor = Color.Yellow;
}
test1.Dispose();
}while(true);
}
The problem is the application is crashing with this code. If I just remove the loop, it works fine.
I checked while debugging, memory usage of the application keeps on increasing with each iteration of the loop, making the application crash at one point.
What I understood is Dispose() will release all the resources... so memory should not increase with each iteration.
Could someone please help to understand what is causing the memory usage increase?

What I do when I have to deal with hungry processes (eg. pictures manipulation) is to call the garbage collector explicitly.
This is not the cleanest way (costs a lot), but reasonnably used, it does the trick.
void status()
{
// will be used to explicitly call the garbage collector
const int COLLECT_EVERY_X_ITERATION = 10;
// store the current loop number
int currentIterationCount = 0;
do
{
// increase current iteration count
currentIterationCount++;
using (Process test1 = new Process())
{
test1.StartInfo.FileName = "doSomething"; // doSomething is executable
test1.StartInfo.UseShellExecute = false;
test1.StartInfo.CreateNoWindow = true;
test1.StartInfo.RedirectStandardOutput = true;
test1.StartInfo.RedirectStandardError = true;
test1.Start();
string output = test1.StandardOutput.ReadToEnd();
test1.WaitForExit();
if (Regex.IsMatch(output, "DEVICE_READY", RegexOptions.IgnoreCase))
{
pictureBox2.BackColor = Color.Green;
}
else
{
pictureBox2.BackColor = Color.Yellow;
}
}
// Explicitly call garbage collection every 10 iterations
if (currentIterationCount % COLLECT_EVERY_X_ITERATION == 0)
GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);
} while (true);
}

Related

Condition met, but conditional action not executed in coroutine

I have a coroutine like so:
private IEnumerator ShortFlashes()
{
this.startedShortFlashes = true;
this.finishedShortFlashes = false;
const int maxFlashes = 3;
int numFlashesSoFar = 1;
if (numFlashesSoFar > maxFlashes)
{
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
yield break;
}
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
}
When this coroutine is running, I can see messages in the Unity console enumerating the number of short flashes, just as intended. (Debug.Log("Number of short flashes so far: " + numFlashesSoFar);).
However, Debug.Log("All 3 short flashes finished!"); is never executed, even when numFlashesSoFar exceeds maxFlashes.
This is very inconvenient, because in my Update() method, there are some additional actions that I would like to perform if this.finishedShortFlashes is true.
How can I fix this issue?
You've hardcoded the values.
const int maxFlashes = 3;
int numFlashesSoFar = 1;
if (numFlashesSoFar > maxFlashes)
{
//...
}
if (numFlashesSoFar > maxFlashes) is effectively equal to if (1 > 3), which is never true.
I genuinely don't understand why you structured the code the way you did, which makes it hard for me to understand the core issue here.
This makes much more sense:
const int maxFlashes = 3;
int numFlashesSoFar = 1;
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
Note that you don't need the if. When the while loop finishes, you already know that the condition is met (otherwise the while loop would not have finished yet.
I don't understand the purpose of the yield break; in your code. It seems unnecessary, so I removed it.
I think you're missing the point of yield keyword.
Once the execution encounter yield return the execution is transferred back to the calling method and the execution state for the routine is retained. The next time you call that same routine, the execution resumes from where it has been yielded.
More information is available in the official documentation
In that particular case, the line Debug.Log("All 3 short flashes finished!"); is never hit, because when the control steps into the method initially, the numFlashesSoFar variable is set to 1 so the condition is never met. Then it goes into the loop, where the yield keyword is encountered. So the next time, the execution continues from within the loop.
I don't understand why you put that if-statement above the loop. The while loop will continue until the while-statement is false. There is no need for the if-statement, you can simply put the code you have inside your if-statement below the loop:
private IEnumerator ShortFlashes()
{
this.startedShortFlashes = true;
this.finishedShortFlashes = false;
const int maxFlashes = 3;
int numFlashesSoFar = 1;
while (numFlashesSoFar <= maxFlashes)
{
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = true;
yield return new WaitForSecondsRealtime(0.05f);
this.Renderer.enabled = false;
Debug.Log("Number of short flashes so far: " + numFlashesSoFar);
numFlashesSoFar++;
}
Debug.Log("All 3 short flashes finished!");
this.finishedShortFlashes = true;
}
Also, you can see yield return in a Coroutine as a "temporary break in code", because it waits for the next frame to continue the code. Unless you return a WaitForSeconds of course, then it will wait until the given amount of time has passed.

Closing active threads once an error threshold has been exceeded [duplicate]

This question already has answers here:
Question about terminating a thread cleanly in .NET
(8 answers)
Closed 6 years ago.
I have an application which will spin up a List of threads then kick them off one after another until a predefined thread limit has been hit, once this happens it will wait until a thread has finished before starting another. I want to implement a way to exit all threads that are currently running if a threshold of consecutive errors is reached. I have got as far as clearing the list of threads to process, so no more will be started but I'm not sure how to go about closing the remaining threads that are running once the thread limit has been reached, I recognise one thread can't talk to another and that's where Im getting hung up..I've provided a cut down version of main method below.
public static void Run(string distinguishedName, string hrExtractFile, string sanctionsFileLocation, string exemptionsFileLocation, string url, string soapAction, int threadLimit)
{
UsersList Employees = new UsersList(distinguishedName, hrExtractFile); //imports users from HR file
int errorcount = 0;
ManualResetEvent resetEventThreadComplete = new ManualResetEvent(false);
ManualResetEvent resetEventNoMoreThreads = new ManualResetEvent(false);
List<Thread> threads = new List<Thread>();
int toProcess = Employees.Count;
for (int i = 0; i < Employees.Count; i++)
{
int current = i;
threads.Add(new Thread(delegate ()
{
User u = Employees[current];
User drUser = new User();
try
{
drUser = fetchUser(u, url, soapAction);
bool userExists = false;
if (drUser != null)
{
userExists = true;
}
//removes a user if they're in the deleted users OU as well as being in system
if (u.IsDeleted)
{
if (userExists == true)
{
Console.WriteLine("Removing " + u.AccountName);
Log.writeToLogs("activitylog.txt", "Removing " + u.AccountName + ":", u, drUser);
DeleteUser(u, url, soapAction);
}
}
errorcount = 0;
}
}
catch (Exception e)
{
if (errorcount <= 5)
{
Log.writeToLogs("syslog.txt", e.ToString());
Log.writeToLogs("activitylog.txt", u.AccountName + " - Failed to true up!");
Console.WriteLine("Failed on " + u.AccountName + ": An error occured, check logs for details");
errorcount++;
}
else
{
lock (_syncObject)
{
//removes threads from list of threads that are pending
if (threads.Count > 0)
{
threads.Clear();
}
}
}
}
resetEventThreadComplete.Set();
if (Interlocked.Decrement(ref toProcess) == 0)
resetEventNoMoreThreads.Set();
}));
}
/*
* Kicks off the first x number of threads (where x = threadLimit) and removes them from list of pending threads
*/
for (int i = 0; i < threadLimit; i++)
{
if (threads.Count < 1)
break;
//runningThreads++;
threads[0].Start();
threads.RemoveAt(0);
}
/*
*Controls the initiation of thread exection; When one thread finishes, another will be started and removed from the list of pending threads
*/
while (threads.Count > 0)
{
resetEventThreadComplete.WaitOne();
resetEventThreadComplete.Reset();
threads[0].Start();
threads.RemoveAt(0);
}
if (toProcess > 0) resetEventNoMoreThreads.WaitOne();
//Log.sendLogs();
}
Assuming you don't mind killing your threads (not usually a great idea), you could use some code like this, just before clearing the list:
if (threads.Count > 0)
{
threads.ForEach(t => t.Abort()); //Kill all threads
threads.Clear();
}
That being said, you should probably not use Thread.Abort(), if you believe this poster:
Did you read the "remark" section on the MSDN? NEVER USE ABORT. IT IS INTENDED AS LAST RESORT. IF YOU USE THREAD.ABORT, SKY MAY FALL DOWN, AND KITTEN WILL BE KILLED
Whatever method you choose, you can put it into that ForEach expression, e.g. if you prefer to use Interrupt:
threads.ForEach(t => t.Interrupt());
Another (far simpler) approach is to modify your threads to check a global flag variable and exit cleanly when it is set. In your main program, when you wish to kill the threads, just set the flag. Be sure to use a volatile variable, or better yet, a WaitHandle.

Worker Threads Blocking When ConcurrentQueue has too many items

This is a weird one, I have a Thread[] of worker threads which each process items in a ConcurrentQueue<string> until the queue is empty, at which point the rest of the program continues.
This works until about ~1500 items at which point all threads stay blocked in the WaitSleepJoin state and never process any of the items in the queue.
I've tried stepping through my code and it appears that the threads are still created, still started and are alive but get blocked immediately and never run their relevant function.
I'm completely flummoxed so any help would be appreciated!
The relevant sections of code are below:
Main Thread Segment:
ConcurrentQueue<string> convertionQueue = new ConcurrentQueue<string>();
List<Thread> converterThreads = new List<Thread>();
Directory.GetFiles(_folderOne, "*.fdf", SearchOption.AllDirectories).ToList().ForEach(file => convertionQueue.Enqueue(file));
Directory.GetFiles(_folderTwo, "*.fdf", SearchOption.AllDirectories).ToList().ForEach(file => convertionQueue.Enqueue(file));
int filesDone = 0;
int totalFiles = convertionQueue.Count;
progressBar.Maximum = totalFiles;
panel1.Visible = true;
for (int i = 0; i < Environment.ProcessorCount; i++)
{
converterThreads.Add(new Thread(() => ConvThreadWorker(convertionQueue, ref filesDone)));
}
converterThreads.ForEach(thread => thread.Start());
DateTime lastTick = DateTime.Now;
int lastFilesDone = 0;
int[] valuesSpeed = { 1, 1, 1, 1, 1 };
int[] valuesTime = { 1, 1, 1, 1, 1 };
int counter = 0;
while (converterThreads.Any(thread => thread.IsAlive))
{
TimeSpan t = DateTime.Now - lastTick;
int deltaFiles = filesDone - lastFilesDone;
double speed = (float)t.TotalMilliseconds <= 0.0 ? 0.0 : deltaFiles / (float)t.TotalMilliseconds;
double tMinus = speed <= 0 ? 0.0 : (totalFiles - filesDone) / speed;
int currentSpeed = (int)(speed * 1000);
int currentTime = (int)(tMinus / 1000);
valuesSpeed[counter] = currentSpeed;
valuesTime[counter] = currentTime;
lblFilesLeft.Text = string.Format("{0}/{1}", filesDone, totalFiles);
lblSpeed.Text = valuesSpeed.Sum() / 5 + " /s";
lblTime.Text = valuesTime.Sum() / 5 + " s";
lblFilesLeft.Update();
lblSpeed.Update();
lblTime.Update();
progressBar.Value = filesDone;
progressBar.Update();
lastTick = DateTime.Now;
lastFilesDone = filesDone;
counter = ++counter % 5;
Thread.Sleep(500);
}
Worker Function:
private void ConvThreadWorker(ConcurrentQueue<string> queue, ref int fileCounter)
{
while (!queue.IsEmpty)
{
string file;
if (queue.TryDequeue(out file))
{
ConvToG(file);
fileCounter++;
}
}
}
Convertion Function:
private void ConvToG(string file)
{
MessageBox.Show("Entering Convertion Function");
if (!_fileCreationDictionary.ContainsKey(file))
{
DateTime lastTimeModified = File.GetLastWriteTime(file);
_fileCreationDictionary.AddOrUpdate(file, lastTimeModified, (key,oldvalue)=>lastTimeModified);
}
ProcessStartInfo procStart = new ProcessStartInfo
{
Arguments = file,
UseShellExecute = true,
FileName = Fdfg,
WindowStyle = ProcessWindowStyle.Hidden
};
Process process = new Process {StartInfo = procStart};
MessageBox.Show("Starting convertion process");
process.Start();
process.WaitForExit();
MessageBox.Show("Finished");
}
The confusing part appears to be how this all revolves around the number of items in the queue, yet there appears to be no overflow.
UPDATE: Adding the mbox's shows that it freezes on the process.Start() section of code, with no errors and will not proceed past that point.
UPDATE 2: If UseShellExecute = false the code works. Which is very confusing to say the least.
I have done something similar with threads spawning processes to collate data. I had issues around the actual process starting and hanging. What I did to get my program working was something like this:
using (Process process = Process.Start(startInfo)) {
if(process.WaitForExit(timeOutMilliseconds)) {
MessageBox.Show("Process exited ok");
//...snip
} else {
MessageBox.Show("Process did not exit in time!");
//...snip
process.Kill();
}
}
There is a bit more going on in the background, regarding limiting the number of running process, etc, but I found that occasionally, for an unknown reason, that I would see several process in the task manager just hanging around forever.
Hope that helps?

for loop keeps making my program crash c#

I have a application which programs firmware to a circuit board. In the application you can program a single board, or a tray. When programming a tray you can only load 14 at a time.
The user may want to program say 30 boards, so I want the program to program the 14 boards and then tell the user they need to reload a tray.
At the moment I only have one board to practice on, so I have just been reprogramming the same one pretending its a tray.
I have tried to resolve this using loops but when I press the start button it all freezes and stops responding.
The following is my code:
private void setFirmwareMultiple()
{
clearTicksandCrosses();
string firmwareLocation = Firmware(productComboBox.Text); //get the firmware location
string STPath = #"C:\Users\Falconex\Documents\FalconexTest\FalconexTest\ST-LINK Utility\ST-LINK_CLI.exe"; //file location
string result; //set string
string result2; //set string
int counter = 0;
int numberOfBoards = int.Parse(numberOfBoardsTextBox.Text);
while (numberOfBoards > counter) {
for (int i = 0; i > 14; i = i + 1) {
ProcessStartInfo start = new ProcessStartInfo(); //new process start info
start.FileName = STPath; //set file name
start.Arguments = "-C -ME -p " + firmwareLocation + " -v -Run"; //set arguments
start.UseShellExecute = false; //set shell execute (need this to redirect output)
start.RedirectStandardOutput = true; //redirect output
start.RedirectStandardInput = true; //redirect input
start.WindowStyle = ProcessWindowStyle.Hidden; //hide window
start.CreateNoWindow = true; //create no window
string picNumber = i.ToString();
using (Process process = Process.Start(start)) //create process
{
programmingTextBlock.Text = "Board Programming...";
System.Windows.Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
try
{
while (process.HasExited == false) //while open
{
process.StandardInput.WriteLine(); //send enter key
}
using (StreamReader reader = process.StandardOutput) //create stream reader
{
result = reader.ReadToEnd(); //read till end of process
File.WriteAllText("File.txt", result); //write to file
}
saveReport();
}
catch { } //so doesn't blow up
finally
{
int code = process.ExitCode; //get exit code
codee = code.ToString(); //set code to string
File.WriteAllText("Code.txt", codee); //save code
if (code == 0)
{
tick1.Visibility = Visibility.Visible;
counter = counter + 1;
}
else
{
cross1.Visibility = Visibility.Visible;
}
programmingTextBlock.Text = "";
}
}
System.Windows.MessageBox.Show("Load new boards");
}
}
}
I have put the total amount of boards the user wants in the for loop.
I think it maybe to do with the for loop. Because at first, in the for loop, I accidently put (i<14) and it caused it to run fine, however it then didn't stop.
Any help would be massively appreciated!
Thank you in advance,
Lucy
As the code stands now, your for loop's content never gets executed. The condition in the for loop is a continue condition. Since i is initialized with 0 the condition i > 14 is never met. So the result is an infinite outer while loop.
Your first "accident" with i < 14 was correct. But then the loop did not stop, because your inner while loop never finishes:
while (process.HasExited == false) //while open
{
process.StandardInput.WriteLine(); //send enter key
}
At first, please don't compare a bool to true or false. A simple while (!process.HasExited) is enough.
Secondly, you have to refresh your process instance to update the HasExited property correctly:
while (!process.HasExited) //while open
{
process.StandardInput.WriteLine(); //send enter key
process.Refresh(); // update the process's properties!
}
You may also consider to add a Thread.Sleep(...) in that loop.
The answer is simple:
while (numberOfBoards > counter) {
for (int i = 0; i > 14; i = i + 1) {
In the code above, the for loop will never be executed, because i will be always less than 14.
Because this, counter will never increment, and than, the while will never finish.
But besides this, your approach to the looping is wrong. The following example (fully test program) is something you should do instead:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int counter = 0;
int i = 0;
int numberOfBoards = 35;
for (; numberOfBoards > counter; i++, counter++)
{
Console.WriteLine("Counter {0}/i {1}", counter, i);
//call your thread here.
//make sure that he exists.
//use somekind of timeout to finish
//alert the user in case of failure but move to the next anyway to avoid an infinite looping.
if (i == 13) i = 0;
}
Console.WriteLine("Press any key");
Console.ReadKey();
}
}
}

C# Process.StartInfo.RedirectStandardInput=true does not return redirected output nor error

I have some code that launches a process "python.exe", the redirected output would return stream from the process if I set process.StartInfo.RedirectStandardInput=false, output is available and processed by readOut() or readErr() thread handlers. However, if I set it to true I wouldn't get any output from the process. I need the input redirection so I can send inputs to the process from a Windows form.
I do have 2 threads one processes the redirected output the other processes the redirected stderror. I appreciate if you can provide some pointers. Thank you.
My code goes like this:
....
Process p = new Process();
p.StartInfo.WorkingDirectory = "C:\\";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
//output is available and processed by readErr if this set to false.
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.StartInfo.FileName = this.exe;
readTh = new Thread(readOut);
readTh.Name = "CmdStdOutTh";
errTh = new Thread(readErr);
errTh.Name = "CmdStdErrTh";
lock (this)
{
p.Start();
readTh.Start();
errTh.Start();
}
....
void readOut()
{
char[] buf = new char[256];
int n = 0;
while ((!p.HasExited || (p.StandardOutput.Peek() >= 0)) && !abort) {
n = p.StandardOutput.Read(buf, 0, buf.Length - 1);
buf[n] = '\0';
if (n > 0)
processOutput(new string(buf));
Thread.Sleep(0);
}
}
void readErr() {
char[] buf = new char[256];
while ((!p.HasExited || (p.StandardError.Peek() >= 0)) && !abort) {
int n = p.StandardError.Read(buf, 0, buf.Length - 1);
buf[n] = '\0';
if (n > 0)
processError(new string(buf));
Thread.Sleep(0);
}
}
Make sure to wait until p is done, using
p.WaitForExit();
That seems to be missing from your example. As far as I can tell the rest is correct, though perhaps could be written better: as written, it looks like if there's no output available your code would spin, waiting. That's going to burn CPU unnecessarily. Instead, just go ahead and call Read: it'll block until there's enough, so this will release the CPU for other threads or processes.
I have figured out the problem. The "p.StartInfo.RedirectStandardInput = true" is working correctly. The problem is in Python.exe. I've to use the "-i" arg option for StartInfo.Arguments. It's explained in
Redirect Python standard input/output to C# forms application

Categories

Resources