How to read/write on console at the same time using threads - c#

I want to implemente a C# Console Application that runs a simulation. Also, I want to give to the user the oportunity to acelerate/decelerate the speed of the simulation pressing '+' or '-' on console.
Is there a way to read the console while writing on it? I believe I could use multithreading for this, but I don't how to do it (I'm still new on C#).
Thank you very much!

You can check Console.KeyAvailable prior to calling Console.ReadKey(). This will let you check the console to see if there is input waiting (ie: the user pressed + or -) without blocking. If you just don't try to read if there is no input available, your main thread will never block waiting on the user.
Using this mechanism, you can actually do this in a single threaded application.

Yes, there is a way to read/write at the "same time". There are a couple of ways to do it:
Use another Thread:
First, you start a thread that is responsible for writing to the console.
Thread t = new Thread(()=>{RunSimulation();});
t.IsBackground = true;
t.Start();
The simulation method would look something along the lines of:
public void RunSimulation()
{
while(running)
{
// Puts the thread to sleep depending on the run speed
Thread.Sleep(delayTime);
Console.WriteLine("Write your output to console!");
}
}
Second, you can continually let the main thread poll for user input in order to make adjustments.
string input = string.Empty;
while(input.Equals("x", StringComparison.CurrentCultureIgnoreCase)
{
input = Console.ReadKey();
switch(input)
{
case "+":
// speeds up the simulation by decreasing the delayTime
IncreaseSpeed();
break;
case "-":
// slows down the simulation by decreasing the delayTime
DecreaseSpeed();
break;
default:
break;
}
}
Use a Timer:
Another approach is to use a [Timer][1] and adjust the frequency of callbacks on the timer instead of adjusting the sleep time on a thread:
// Create the timer
System.Timers.Timer aTimer = new System.Timers.Timer(10000);
// Hook up the Elapsed event for the timer.
aTimer.Elapsed += new ElapsedEventHandler(OnPrintSimulationResult);
// Change the Interval to change the speed of the simulation
aTimer.Interval = 2000; // <-- Allows you to control the speed of the simulation
aTimer.Enabled = true;
Of course, you have to deal with thread safety, but this should give you a decent place to start. You can come back once you try one of those approaches and you're having a specific problem with it, I'm sure people would be happy to address any particular issues you have. Note that it will not be a very graceful-looking solution to do it in the console, but it will work. If you want something more graceful, then simply make a GUI application that has a text area, redirect the console output to the text area and add 2 buttons (+/-) to adjust the speed.
[1]: http://msdn.microsoft.com/en-us/library/system.timers.timer.aspx

Related

Keeping console app from closing but still run codes

I have a console app that does not terminate using a code
new System.Threading.AutoResetEvent(false).WaitOne();
What I want to achieve: I would want to run a StopWatch and if it meets a condition it will run certain file manipulating codes. And then finally after the block of code, resets the timer and wait for it to be true again to rerun.
Problem: However, upon debugging I cant get my code to go through my conditions even it has already passed the required condition.
My Code:
static void Main(string[] args)
{
string mutex_id = "41585f436f766572743243494d";
using (System.Threading.Mutex mtx = new System.Threading.Mutex(false, mutex_id))
{
if(!mtx.WaitOne(0,false))
{
return;
}
processTimer = new Stopwatch();
processTimer.Start();
if (processTimer.Elapsed.Seconds > 10)
{
processTimer.Stop();
fileQueue = Directory.GetFiles(ConfigurationManager.AppSettings["WatchPath"], ConfigurationManager.AppSettings["Format"]).ToList();
}
//process the fileQueue
//..
//..
//processTimer.Reset(); -> Reset Timer to wait for another 10 sec and process again
new System.Threading.AutoResetEvent(false).WaitOne();
}
}
I have used a FileSystemWatcher before but I failed to get the process correctly(Like Consecutive/Concurrent file creations and such). Tried Threading and Timers as my question.
Now I'm trying to approach this issue from a new perspective. Hope some can enlighten me with this.
There is no "try again" in your code.
The code you've written does the following:
Create a mutex and lock it
If it already exists, close application
Start a stopwatch
Check if 10 seconds elapsed (which they didn't)
Create a new AutoResetEvent and wait for ever for it
You will need some loop that periodically checks if 10 seconds have passed and otherwise Sleep

A thread for updating a RichTextBox.Text twice a second is not working

First of all - I'm very low skilled programmer. I am building the foundation of a simple music app for my bachelor degree project. My question is regarding a internal clock method that is meant to increase an int value by 1 BPM times a minute.
I've created an internalClock class:
public class internalClock
{
// THIS METHOD WILL BE CALLED WHEN THE THREAD IS STARTED
public static void clockMethod()
{
int BPM = 135;
int clockTick = 1;
Form1 clockForm = new Form1();
// infinite loop
while (true)
{
if (clockTick == 8)
{
clockTick = 1;
}
else
{
clockTick++;
}
clockForm.metrobox.Text = clockTick.ToString();
Thread.Sleep(60 * 1000 / BPM);
}
}
}
This is how I managed to get an access to the RichTextBox itself:
public RichTextBox metrobox
{
get { return metroBox; }
set { metroBox = value; }
}
In the main 'Program.cs' I've written what's meant to start a separate thread to run the clockMethod until the program is closed:
// THREADING
// Create a thread
internalClock oClock = new internalClock();
Thread oClockThread = new Thread(new ThreadStart(internalClock.clockMethod));
// Start the internalClock thread
oClockThread.Start();
It's not updating the text in the RichTextBox. Also, if I call the clockMethod() without creating a separate thread for it - the application freezes. Sorry for my amateur question, I'm just getting started with C# (yeah.. my uni is useless). What am I doing wrong in my code?
So the above code has several problems, however I would encourage you to check out the Timer control that you can add to the form you want to do processing at a certain interval or in certain "ticks". MSDN Form Timer
With the timer you can remove that class you have and invoking a new thread, etc etc. I would read up on the Timer class in the given link and think about how you can re-work your application structure to fit that. The concepts for why that thread isn't working, etc, is frankly not that important for where you're at. I think you just need to focus for now on a tool that already does what you want it to do, which I believe is the Timer.
As a general note, you usually don't need to create a raw thread in .NET. As of .NET 4.0 you can access types called Tasks to perform multi-threaded logic and processing. If you find the need to do that later on, check that out. Task Type MSDN

Threading doesn't work correctly

I have an application, which is to repeat the numbers what I hear then record the digits what I read. The numbers are 0,1,2,3,4,5,6,7,8,9. I use a for loop to play these numbers with a text to speech skill, which is from a third party.
For the recording part, I have to put it in a separate thread by the third party requirement. To record the voice, the method is likely:
recordVoiceResource.Record(fileName);
To stop it, use:
recordVoiceResource.Stop();
Now I find sometimes my recording is 0 second, which means the code perhaps doesn't reach Record line. Sometimes it only has 2 seconds. I believe the thread schedule is wrong.
private ManualResetEvent terminate = new ManualResetEvent(false);
PlayTTS("Please repeat the following numbers as you hear them.");
Thread t = new Thread(() => RecordNumbers());
t.Start();
Thread.Sleep(2000);
terminate.Set();
terminate.WaitOne();
PlayNumbers();
recordVoiceResource.Stop();
The thread method is:
private void RecordNumbers()
{
recordVoiceResource = TelephonyServer.GetVoiceResource();
recordVoiceResource.MaximumSilence = 1;
recordVoiceResource.MaximumTime = 30;
// Start recording what I read from I heard
recordVoiceResource.Record(fileName);
}
To playNumbers,
private void PlayNumbers()
{
foreach (var item in numbers)
{
try
{
vr.PlayTTS(item.ToString()); // will be 0,1,2,...9
Thread.Sleep(2000);
}
According to your comment, the property MaximumSilence gets or sets the maximum silence in seconds that will be allowed until termination of the next voice function. You are setting it to one second, starting the recording, and then sleeping for two seconds before beginning playback that prompts the user to say something. Do you see the problem here? Assuming the mic doesn't pick up some unrelated speech during that period, the recording will stop before the playback even begins.
Since there is a 2-second gap between number playback, you probably need to set MaximumSilence to several seconds.
That is, of course, assuming your intention was to capture a single recording of the user speaking all the numbers (which is how your code is written). If you want to capture the spoken numbers individually, then you may need to schedule and synchronize separate recordings as each number is played back. You may want to double-check the API to make sure your solution is what you intended.
It is very likely your problem is causing due to Thread.Sleep(). Use a timer instead:
System.Timers.Timer Record = new System.Timers.Timer();
Record.Interval = 2000;
Record.Elapsed += new System.Timers.ElapsedEventHandler(Record_Elapsed);
void Record_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Record.Enabled=false;
PlayNumbers();
recordVoiceResource.Stop();
}
And set:
Thread t = new Thread(() => RecordNumbers());
t.Start();
Record.Enabled=true;

Ticker generated forms not displaying correctly

Hopefully this isn't too difficult to follow.
I'm currently working on a small timelogging application that runs quietly in the background. Every time the ticker runs down, the application prompts the user to say what he/she was doing since the last prompt. I'll eventually have the application write the data into a spreadsheet.
One of the options I have so far enables the user to choose whether or not he/she would like to use the default prompting setting (every time a prompt is missed, it stays visible until the next one is created, meaning if the user leaves his/her computer for a while there may be a fair few prompts sitting on the screen waiting to be filled in) or would like to combine all the prompts (every time a prompt is missed and a new one pops up, the old one is closed and the new one covers the time of the old prompt and the new prompt).
The user can also select a tickbox to turn prompts off. When he/she turns prompts back on again, a prompt pops up asking the user to fill in what he/she was doing while prompts were turned off (useful when the user is running fullscreen applications, etc).
My problem is, when I try to generate the prompts, they don't display correctly. I can't manipulate them at all and none of the controls display. They basically look like empty forms.
Here's my code for generating prompts using the ticker:
public void ticker(object source, System.Timers.ElapsedEventArgs e)
{
if (groupMissed)
{
incrementsMissed += 1;
if (incrementsMissed > 1)
{
IncrementForm form = (IncrementForm)Application.OpenForms["IncrementForm"];
if (form.InvokeRequired)
{
form.Invoke(new MethodInvoker(delegate { form.Close(); }));
}
}
}
else
{
incrementsMissed = 1;
}
IncrementForm theIncrementForm = new IncrementForm(this, e.SignalTime);
theIncrementForm.Show();
latestIncrement = e.SignalTime;
}
And here's my code for generating prompts using the "turn prompts off" checkbox:
private void chkbxAlerts_Click(object sender, EventArgs e)
{
if (!chkbxAlerts.Checked)
{
// Ensures that the time missed is covered and restarts the timer
DateTime now;
now = DateTime.Now;
if ((now - latestIncrement).TotalMinutes >= 1) // Only records time if it is equal to or greater than one minute
{
// TO-DO: FIX
if (groupMissed)
{
incrementsMissed += 1;
if (incrementsMissed > 1)
{
IncrementForm form = (IncrementForm)Application.OpenForms["IncrementForm"];
if (form.InvokeRequired)
{
form.Invoke(new MethodInvoker(delegate { form.Close(); }));
}
}
}
else
{
incrementsMissed = 1;
}
IncrementForm theIncrementForm = new IncrementForm(this, now, latestIncrement);
theIncrementForm.Show();
latestIncrement = now;
}
timer.Enabled = true;
}
else
{
// Stops the timer
timer.Enabled = false;
}
}
If you need any further clarification, please let me know. Thanks so much in advance for any help, this has been bugging me.
System.Timers.Timer has a SynchronizingObject property. If you set that to the main form (or the form that contains the timer), then the timer tick event will be raised on the GUI thread.
Do note that System.Timers.Timer has the nasty habit of swallowing exceptions that occur in the Elapsed event. If your tick handler throws an exception, you'll never see it. It's a nasty bug hider. For that reason, I recommend using either System.Windows.Forms.Timer or System.Threading.Timer. If you use the Windows Forms timer, the elapsed event is raised on the GUI thread. If you use System.Threading.Timer, you'll have to use Invoke as NSGaga shows in his answer.
See Swallowing exceptions is hiding bugs for more information about why I discourage the use of System.Timers.Timer.
I think, from what I can see, not 100% but your timer is spawning your windows in a separate thread (being from the timer ticker call).
While theoretically that can work (take a look at this How to open a form in a thread and force it to stay open)
...you may be much better off staying within the main thread.
Try something like this...
yourMainWindow.Invoke(new MethodInvoker(() =>
{
IncrementForm theIncrementForm = new IncrementForm(this, e.SignalTime);
theIncrementForm.Show();
latestIncrement = e.SignalTime;
}));
...that's from your timer - that way (as I can see) you should have it all 'on the main thread' and make things much easier for you.
hope this helps

Check if process is idling

I'm trying to find out when a process has stopped doing his work. I've been trying it with this code but it doesn't notice that the program is still running and processing a file. Probably because it's still doing things that take less then one microsecond:
TimeSpan startTime = m_Process.TotalProcessorTime;
int idleCycles = 0;
int iMax = Math.Max(iMinNoActivityTime/100, 5);
while (idleCycles < iMax)
{
Sleep(100);
TimeSpan curTime = m_Process.TotalProcessorTime;
int delta = curTime.Subtract(startTime).Milliseconds;
if (delta != 0)
{
idleCycles = 0;
}
else
{
idleCycles++;
}
startTime = curTime;
}
It's called for 3000 seconds: 30 consecutive time-blocks of 100 miliseconds without processor activity.
Is there any way to do this so it doesn't see it as idling when it's still running? The process reads the file, deletes it and then processes it so it can't monitor the directory.
I'd go back and look at your design here. Trying to spot when another process is idle doesn't seem like a very solid approach.
Why not just have the other process use a named event to signal when it is done?
If you know that the work is being done on the main (UI) thread, for example for a simple console application, then you can use Process.WaitForInputIdle. You can optionally specify a timeout parameter if you just want to poll. Now, if the work is being done on a background thread, then you're not going to be able to detect it (at least not without resorting to some nasty hacks). As GrahamS points out, it's certainly better to rethink design in thise case.

Categories