I have a console application with two threads, one is doing repetitive time consuming work, the other is checking to see if the user has pressed the ESC key. If the ESC key was pressed, the time consuming work thread is paused, an "are you sure" message appears, and if yes is selected, the time consuming work thread finishes its current loop then exits.
The code I have to check for a key-press is using a lot of CPU resources due to the while (!breakCurrentOperation(work)) ; loop. How can I prevent this from happening?
Code:
public void runTimeConsumingWork()
{
HardWork work = new HardWork();
Thread workerThread = new Thread(() => work.StartWorking());
workerThread.Start(); // Start the hard work thread
while (!workerThread.IsAlive) ; // Hault untill Thread becomes Active
// Check if the user wants to stop the hard work
while (!breakCurrentOperation(work)) ;
// Cancle the hard work
work.Stop();
// Notify the User
UserInterfaceController.WriteToConsole("Operation Cancled...");
}
public static bool breakCurrentOperation(HardWork work)
{
if (Console.KeyAvailable)
{
var consoleKey = Console.ReadKey(true);
if (consoleKey.Key == ConsoleKey.Escape)
{
work.Pause(); // Pause
UserInterfaceController.WriteToConsole("Do you want to stop the current process? \nType s to stop or c to continue.");
string input = Console.ReadLine();
if (input == "c" || input == "C")
{
work.Pause(); // Unpause
return false; // Continue
}
else if (input == "s" || input == "S")
{
return true; // Break the loop
}
else
{
UserInterfaceController.WriteToConsole("Error: Input was not recognized, the current process will now continue. Press Esc to stop the operation.");
work.Pause(); // Unpause
}
}
}
return false;
}
If I place a Thread.Sleep(2000) in the main console UI thread, CPU usage goes way down, but the application becomes unresponsive with a 2 second delay.
Do you really have to constantly poll for input? If you are waiting for input in a separate thread, just use the Console.ReadKey. It will block the input thread, but your other thread will keep processing. You don't seem to be doing anything else on the input thread, so blocking shouldn't be an issue.
Look like your esc key press check logic running in end less loop due to while loop. Due to this the function keep utilizing the system resource.
To overcome this please use some delay in your loop using Thread.Sleep. 1 second delay will improve lot of performance.
Related
I want to interrupt my producer-consumer program by pressing the key T. I searched a lot of answers but i can't figure out why it is not working.
static void Main(string[] args)
{
Buffer buffer = new Buffer();
Produtor prod = new Produtor(buffer);
Thread threadProdutor = prod.CriarThreadProdutor();
Consumidor cons = new Consumidor(buffer, 100000);
Thread threadConsumidor = cons.CriarThreadConsumidor();
threadProdutor.Start();
threadConsumidor.Start();
threadProdutor.Join();
threadConsumidor.Join();
while (Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.T)
{
Environment.Exit(0);
}
}
I added a breakpoint in my while, but the program won't even get there.
By putting the loop after your Join() calls the threads will have already completed by the time you check the console for input, so you'll need to reverse the order.
Further, the while loop will only be entered and continue running if there is a key available and it is T. You want the opposite: loop until a key is available and it is T.
Finally, Console.ReadKey() blocks until a key is pressed, so you don't need to check Console.KeyAvailable, too, unless you want to do something else while you wait for T (such as display progress or check if the threads completed on their own).
while (Console.ReadKey(true).Key != ConsoleKey.T)
{
// Do nothing...
}
// T has been pressed
// Signal to the threads to stop
// Set a flag, Cancel() a CancellationTokenSource, etc.
// Wait for the threads to terminate
threadProdutor.Join();
threadConsumidor.Join();
// Exit the program
Environment.Exit(0);
To display progress while you wait for the interrupt key, you can rewrite the loop like this...
TimeSpan progressInterval = TimeSpan.FromSeconds(1);
// complete is a simple flag set by the consumer(s)
// Only call ReadKey() when KeyAvailable so it can't block longer than updateInterval
while (!complete && (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.T))
{
Console.WriteLine($"Current time is {DateTime.Now:HH:mm:ss.fff}");
Thread.Sleep(progressInterval);
}
Note that this has the downside of always sleeping for the entire progressInterval, even if the exit condition has been satisfied before then. A simple workaround would be to reduce the time between checks to 1 / n and then only display the progress after every nth check...
TimeSpan progressInterval = TimeSpan.FromSeconds(1);
const int ReadsPerProgressInterval = 10;
TimeSpan sleepTimeout = new TimeSpan(progressInterval.Ticks / ReadsPerProgressInterval);
int readCount = 0;
// complete is a simple flag set by the consumer(s)
// Only call ReadKey() when KeyAvailable so it can't block longer than updateInterval
while (!complete && (!Console.KeyAvailable || Console.ReadKey(true).Key != ConsoleKey.T))
{
// This won't display progress until after progressInterval has elapsed
// To display initial progress:
// A) change to == 1, or...
// B) duplicate progress display to before the loop as well
if (++readCount % ReadsPerProgressInterval == 0)
Console.WriteLine($"Current time is {DateTime.Now:HH:mm:ss.fff}");
Thread.Sleep(sleepTimeout);
}
I am creating a console application that shows simple animations with text. I have a thread that waits for the user to press "Up Arrow", "Down Arrow" and "Enter/Carriage Return/Return". It seems that the user must press any of these keys multiple times for the key to be registered, however sometimes the key is registered instantly. I assume, as you would too; that this is a problem with my code. I had a power failure last week and it wiped out part of my project, whilst rewriting my code this problem appeared.
I have tried using
if(Console.KeyAvailable == true && Console.Readkey().key == [one of the three keys])
to no avail.
I tried removing (Memory memory) from the class leaving it just
private static void GetKeyboardInput() {}
but this didn't seem to have any effect. BTW, when I did that the thread was declared like this
Thread getkey = new Thread(GetKeyboardInput);
perhaps I am doing this part wrong?
Here is some of my code neutered for size...
// declare listener thread and start it.
Thread GetKey = new Thread(() => GetKeyboardInput(memory));
GetKey.Start();
while (GetKey.ThreadState == ThreadState.Running)
{ Animate(memory); } // this is the text animation, ---> Menuitem <---
// the arrows move in and out from the menu item
Animate(memory); runs in the current tread, so it isn't the culprit, correct?
// This is the code that the thread runs
// Memory class contains all stored values for this program
private static void GetKeyboardInput(Memory memory)
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
if (Console.ReadKey().Key == ConsoleKey.UpArrow)
{
// move up. There is more code here but not relevant
}
if (Console.ReadKey().Key == ConsoleKey.DownArrow)
{
// Move Down. Same as before
}
}
Thread.CurrentThread.Abort();
}
I expect that in less than 1000ms after a key is pressed that the revelant code within the if statements gets executed and the screen will show a change to the user, if any.
Actual results are that sometimes the program reacts to user input instanly, and other times the user has to press the key multiple times to register a change. This is not session dependent either, at any time during execution both of these above problems will/can be present.
The problem is that you're calling Console.ReadKey() multiple times: once to check that it's not enter, again to check whether it's UpArrow, yet again to check whether it was DownArrow. You probably want to read it once, and store it in a variable.
// This is the code that the thread runs
// Memory class contains all stored values for this program
private static void GetKeyboardInput(Memory memory)
{
while (true)
{
var key = Console.ReadKey().Key;
if (key == ConsoleKey.Enter)
{
break;
}
else if (key == ConsoleKey.UpArrow)
{
// move up. There is more code here but not relevant
// meaning these values effect the animation and nothing else
}
else if (key == ConsoleKey.DownArrow)
{
// Move Down. Same as before
}
}
// No need to call Thread.Abort - exiting this method does the same
}
Meet Thread:
public void TimerFunc(){
...
while (true)
{
...
sound.PlayLooping();
// Displays the MessageBox and waits for user input
MessageBox.Show(message, caption, buttons);
// End the sound loop
sound.Stop();
...
}
}
Thread gets started by a button in the main interface, and can get killed by a button in the interface.
How do i get the soundloop to stop if the thread gets killed when waiting for user input?
You DO NOT kill the thread. If the thread is killed, it can't do anything.
Just politely send a message to the thread, asking it to stop playing.
private volatile bool canContinue = true;
public void TimerFunc(){
...
while (true && canContinue)
{
...
sound.PlayLooping();
// Displays the MessageBox and waits for user input
MessageBox.Show(message, caption, buttons);
// End the sound loop
sound.Stop();
...
}
}
public void StopPolitely()
{
canContinue = false;
}
The button on the main interface will then just call thread.StopPolitely() and terminate the thread in a clean way.
If you want it to terminate faster, you may consider other and more aggressive solutions, such as checking canContinue more often, or using Thread.Interrupt() to wake the thread even if it's busy in a blocking call (but then you have to manage the interrupt)
Since it's just a bool, and it's single-writer/single-reader, you can even avoid declaring it as volatile, even though you should.
I'm currently making a program to simulate a set of ATMs in visual C#. It's supposed to stop somebody accessing their account if it has already been accessed from a different location. Is it possible to show a message that the account has already been accessed while a semaphore is waiting?
Here is the part of the code where the semaphore is used:
private void button1_Click(object sender, EventArgs e)
{
count++;
if (count == 1)
{
account = findAccount();
if (findAccount() != 5)
{
textBox1.Text = "Please Enter Your Pin";
}
else
{
textBox1.Text = "Please Enter Your Account Number";
count = 0;
}
textBox2.Clear();
}
if (count == 2)
{
if (findPin(account) == true)
{
semaphore.WaitOne();
textBox1.Text = "1: Take Out Cash \r\n2: Balance \r\n3: Exit";
}
else
{
semaphore.Release();
textBox1.Text = "Please Enter Your Account Number";
count = 0;
}
textBox2.Clear();
}
if (count == 3)
{
atm();
}
if (count == 4)
{
withdraw();
}
if (count == 5)
{
int value = Convert.ToInt32(textBox2.Text);
customWithdrawl(value);
}
}
Consider doing two calls to WaitOne. The first call will have a timeout of zero and return a bool that will tell you whether or not you got the semaphore, or someone else still owns it. Two things can happen from there:
1) If someone else owns it, pop up a message that says "Someone else owns the semaphore" and call WaitOne again, but without a timeout (like you're doing now). After the 2nd call to WaitOne returns, close the window that you popped up a second ago..
2) If your call to waitOne with 0 timeout returns true, then you got the semaphore on the 1st try. No need to pop up a window.
Example:
if( semaphore.WaitOne(0) ) //This returns immediately
{
//We own the semaphore now.
DoWhateverYouNeedToDo();
}
else
{
//Looks like someone else already owns the semaphore.
PopUpNotification();
semaphore.WaitOne(); //This one will block until the semaphore is available
DoWhateverYouNeedToDo();
CloseNotification();
}
semaphore.Release();
Note, there are some other issues lurking here.
You probably want to use a try/finally block to release the semaphore to ensure that it gets released across all exception paths.
It's also probably a bad idea to call semaphore.WaitOne() from the GUI thread because the application will become non-responsive while it waits. In fact, you may not see the result of PopUpNotification() if you've hung the GUI thread while doing the 2nd Wait. Consider doing the long wait on a 2nd thread and raising an event back on the GUI thread once you own the semaphore
Consider the following design to resolve Issue 2:
private void button1_Click(object sender, EventArgs e)
{
if(AcquireSemaphoreAndGenerateCallback())
{
//Semaphore was acquired right away. Go ahead and do whatever we need to do
DoWhateverYouNeedToDo();
semaphore.Release()
}
else
{
//Semaphore was not acquired right away. Callback will occur in a bit
//Because we're not blocking the GUI thread, this text will appear right away
textBox1.Text = "Waiting on the Semaphore!";
//Notice that the method returns right here, so the GUI will be able to redraw itself
}
}
//This method will either acquire the semaphore right away and return true, or
//have a worker thread wait on the semaphore and return false. In the 2nd case,
//"CallbackMethod" will run on the GUI thread once the semaphore has been acquired
private void AcquireSemaphoreAndGenerateCallback()
{
if( semaphore.WaitOne(0) ) //This returns immediately
{
return true; //We have the semaphore and didn't have to wait!
}
else
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Waiter));
return false; //Indicate that we didn't acquire right away
}
}
//Wait on the semaphore and invoke "CallbackMethod" once we own it. This method
//is meant to run on a background thread.
private void Waiter(object unused)
{
//This is running on a separate thread
Semaphore.WaitOne(); //Could take a while
//Because we're running on a separate thread, we need to use "BeginInvoke" so
//that the method we're calling runs on the GUI thread
this.BeginInvoke(new Action(CallbackMethod));
}
private void CallbackMethod()
{
textBox1.Text = string.Empty; //Get rid of the "Waiting For Semaphore" text. Can't do this if we're not running on the GUI thread
DoWhateverYouNeedToDo();
semaphore.Release();
}
Now, this solution could also be fraught with peril. It's kind of hard to follow the execution of the program because it jumps around from method to method. If you have an exception, it could be difficult to recover from and make sure all of your program state is correct. You also have to keep track of things like the account number and the pin numbers through all of these method calls. In order to do that, Waiter and CallbackMethod should probably take some parameter that tracks this state that gets passed along to each step. There's also no way to abort waiting (a time out). It will probably work, but shouldn't make it into any production code because it would be too difficult to maintain or extend.
If you really wanted to do it right, you should consider encapsulating the ATM logic in an object that will raise events that the GUI can subscribe to. You could have a method like ATM.LogInAsync(Account,Pin) that you could call. This method would return immediately, but some time later, an event on the ATM class like "LogInComplete" would fire. This event would have a custom EventArgs object that would contain data to trace which log-in has occurred (mainly the Account number). This is called the Event-based Asynchronous Pattern
Alternatively, if you're using C# 5.0, you can use the new Async/Await syntax in the AcquireSemaphoreAndGenerateCallback() method. That's probably the easiest way because the compiler will handle most of the complexities for you
Yes, you may show your message/form/messagebox right before the Wait method. Then when it receives the signal to unblock, you hide your message.
I'm trying to figure out how I can use Console.ReadLine and a timer. My console program is designed to run a long process automatically, with this process restarting every 30 seconds after the previous process completed. I want to give the user the ability to break the auto-run by typing a command though. If I use Console.ReadLine() though, it will wait until the user enters something, whereas I want the program to continue on through a loop if nothing is entered within 30 seconds. . . Any thoughts??
For example:
RunProcess > Wait 30s for User Input. If none: Continue Loop
Thanks a lot!
You could run your timer on a separate thread. When the user enters text, store it in a variable that is accessible to both threads. When the timer ticks, see if anything is entered and continue accordingly.
Be sure to be thread safe :-)
EDIT:
You can use a System.Threading.Timer to tick every 30 seconds and in its callback method, check if the text has been set.
Don't use Console.ReadLine() but check if Console.KeyAvailable is true and then read Console.ReadKey() to check for exit condition.
Try this example code
class Program
{
static bool done;
static void Main(string[] args)
{
int count = 0;
done = false;
while (!done)
{
Thread.Sleep(2000);
count++;
Console.WriteLine("Calculation #" + count.ToString());
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey();
if (key.Key == ConsoleKey.Escape)
{
done = true;
}
}
}
Console.WriteLine();
Console.WriteLine("end");
}
}