Checking a specific key pressed in a loop (console application) [duplicate] - c#

I know I can use ReadKey for that but it will freeze the app until user presses a key. Is it possible (in console app) to have some loop running and still be able to react? I can only think of events but not sure how to use them in console.
My idea was that the loop would check for input during each iteration.

They way I have done this for my own application was to have a dedicated thread that calls into System.Console.ReadKey(true) and puts the keys pressed (and any other events) into a message queue.
The main thread then services this queue in a loop (in a similar fashion to the main loop in a Win32 application), ensuring that rendering and event processing is all handled on a single thread.
private void StartKeyboardListener()
{
var thread = new Thread(() => {
while (!this.stopping)
{
ConsoleKeyInfo key = System.Console.ReadKey(true);
this.messageQueue.Enqueue(new KeyboardMessage(key));
}
});
thread.IsBackground = true;
thread.Start();
}
private void MessageLoop()
{
while (!this.stopping)
{
Message message = this.messageQueue.Dequeue(DEQUEUE_TIMEOUT);
if (message != null)
{
switch (message.MessageType)
{
case MessageType.Keyboard:
HandleKeyboardMessage((KeyboardMessage) message);
break;
...
}
}
Thread.Yield(); // or Thread.Sleep(0)
}
}

Have the loop run in separate thread.
class Program
{
private static string input;
public static void Main()
{
Thread t = new Thread(new ThreadStart(work));
input = Console.ReadLine();
}
private static void work()
{
while (input == null)
{
//do stuff....
}
}
}

Try KeyAvailable property starting with .NET Framework 2 through now (current .NET 6 - including .NET Core). A single thread can process in a loop without being blocked.
// loop start
if (Console.KeyAvailable) // Non-blocking peek
{
var key = Console.ReadKey(true);
// process key
}
// continue without stopping
// loop end

Related

Not getting expected output in the Multithreading question

Task description:
Write a program that reads an positive integer value n (n > 3), then
creates n threads (each thread has id; id starts from 1) and works
until it receives a stop signal. All of n threads are waiting for a
signal. Every second main thread sends a signal for a random thread,
then that thread should print its id and return to a waiting state.
Requirements:
All additional threads should be finished correctly. At the thread
function exit, a message about exit should be printed. While the
thread is waiting for the condition variable, spurious wakeup should
be checked. Only std::cout allowed for text output. Stop signal is
SIGINT (ctrl+c).
I have written the following code for the above question but in output, all the threads are not exiting. I am not able to figure out the problem as I am new to this topic. Any kind of help will be really appreciated.
class Program
{
public static void Main()
{
var numberofthreads = Convert.ToInt32(Console.ReadLine());
ProcessingClass myobject = new ProcessingClass();
myobject.createThreads(numberofthreads);
}
}
public class ProcessingClass
{
public Mutex mymutex = new Mutex();
private bool thread_flag = false;
public void createThreads(int numberofthreads)
{
var threads = new List<Thread>(numberofthreads);
for (int i = 0; i < numberofthreads; i++)
{
Thread th = new Thread(() =>
{
threadsworking();
});
th.Name = "Thread" + i;
th.Start(); // <-- .Start() makes the thread start running
threads.Add(th);
}
Console.CancelKeyPress += (object sender, ConsoleCancelEventArgs e) =>
{
var isCtrlC = e.SpecialKey == ConsoleSpecialKey.ControlC;
if (isCtrlC)
{
thread_flag = true;
int num = 1;
foreach (var thread in threads)
{
thread.Join();
Console.WriteLine($"Thread {num} exits");
num++;
}
}
e.Cancel = true;
};
}
public void threadsworking()
{
while (thread_flag == false)
{
mymutex.WaitOne(); // Wait until it is safe to enter.
Console.WriteLine("{0}", Thread.CurrentThread.Name);
Thread.Sleep(1000); // Wait until it is safe to enter.
mymutex.ReleaseMutex(); // Release the Mutex.
}
}
}
enter image description here
Consider preventing mutex from blocking threads from exiting.
When you use mutex.WaitOne() it blocks execution until the Mutex is owned by that thread. This can be really helpful for ensuring a thread has exclusive control over a shared resource. However, where this becomes a problem is when you want to arbitrarily end those threads such as when you invoke the event on the Console.CancelKeyPress.
You can see the effects of this by logging before and after the thread.Join() call you do in the event.
thread_flag = true;
int num = 1;
foreach (var thread in threads)
{
Console.WriteLine($"Joining {thread.Name}");
thread.Join();
Console.WriteLine($"Joined {thread.Name}");
Console.WriteLine($"Thread {num} exits");
num++;
}
When we do that logging it will show us that when you call Join() on Thread # 1 you see Joining 1. Then there is a really long pause, other threads still are doing work, and then finally all the threads join back to back.
The reason for this is - while Join() is waiting for Thread 1 to finish, Thread 1 is still waiting for the mutex.
Even though you set the thread_flag flag to true, Thread 1 can't exit because it hasn't taken ownership of the mutex to perform it's work and eventually exit the while() loop.
We can solve this issue fairly simply.
Consider using a timeout when waiting for the mutex
When you call .WaitOne(n) on the mutex you can wait for n given milliseconds and give up taking ownership of the mutex.
This will allow more frequent evaluations of the while loop, and subsequently more times that the threadsworking method checks to see if it should exit(using the thread_flag flag).
Heres a short example how implementing that change might look
public void threadsworking()
{
while (thread_flag == false)
{
// wait to enter the mutex, give timeout to prevent blocking
// until mutex opens and use the bool returned to determine
// if we should release the mutex or not
if (mymutex.WaitOne(1))
{
try
{
Console.WriteLine("{0}", Thread.CurrentThread.Name);
Thread.Sleep(1000); // Wait until it is safe to enter.
}
finally
{
// make sure even if we encounter an error the mutex is released
mymutex.ReleaseMutex(); // Release the Mutex.
}
}
// allow other threads to continue their work instead of blocking with a while loop, this is optional depending on the workload
Thread.Yield();
}
}

Terminating a thread immediately and safely (C#)

I'm in the process of writing a simple "Score Attack" Poker game. A player assembles poker hands which are worth points as a timer ticks down. My problem is a game over scenario.
My game logic runs in a single thread since the game itself is so simple. I need to know how to terminate that thread as it is, with the player no longer able to make input. I've read on the MSDN that the safe way to do this is to use a loop to cause the thread's method to return and end the thread. The problem I run into is that my game requires user input, and the user input would cause the loop to not be checked at the moment the timer ticks to zero.
The code so far uses the Thread.Abort(), and it works, but from my searching on this site that is universally regarded as a bad idea. Is there any way I could set a condition that would terminate the thread safely regardless of methods within said thread needing input? (Console.ReadLine())
Code for the game loop and timer callback that aborts the thread:
private void GameLoop()
{
double stash = 0;
while (true)
{
player.SwapCards(gameDeck);
Table.WriteInfo("Stash This Hand? y/n");
if (Console.ReadLine().Equals("y"))
{
countdown += (int)ScoreHand(player.Hand);
stash += ScoreHand(player.Hand);
BankHand();
}
}
}
private void TimeDrop(object state)
{
countdown--;
Debug.WriteLine(countdown);
if (countdown == 0)
{
GameThread.Abort();
GameOverThread.Start();
Timer.Dispose();
}
}
As it sits the loop simply runs until the thread is aborted.
Edit:
Upon request, the code the starts the threads:
public Game()
{
gameDeck = new Deck();
InitPlayer();
DealHand();
countdown = 60;
GameThread = new Thread(GameLoop);
GameOverThread = new Thread(GameOver);
Timer = new Timer(new TimerCallback(TimeDrop), null, 0, 1000);
Timer.Change(0, 1000); //Ensures timer won't be garbage collected
GameThread.Start();
}
This sort of thing can be implemented easier and cleaner using async/await rather than threads.
First we need to wrap the blocking console input method with one that is cancellable (and async). The method polls the console using KeyAvailable and asynchronously delaying while checking the CancellationToken.
public static async Task<ConsoleKeyInfo> ReadKeyAsync(CancellationToken cancellationToken)
{
while (!Console.KeyAvailable)
{
await Task.Delay(100, cancellationToken);
}
return Console.ReadKey();
}
Now we can start this async method and pass a cancellation token from a CancellationTokenSource that will automatically cancel after a specific amount of time (10 seconds as an example).
public static async Task Main(string[] args)
{
Console.WriteLine("You have 10 seconds to press the Y key...");
var cts = new CancellationTokenSource(10_000);
try
{
while (true)
{
var key = await ReadKeyAsync(cts.Token);
if (key.Key == ConsoleKey.Y)
{
Console.WriteLine("Good job!");
break;
}
else
{
Console.WriteLine("Wrong Key");
}
}
}
catch (OperationCanceledException)
{
Console.Write("Time up!");
}
}

Thread Not Starting in C# Console Application

I am trying to get a thread to run in the following unfinished code. The basics of which are as follows; when the console app starts, it should start a thread which will go off, navigate to a web page (which will eventually do some processing) before stopping and killing off the separate thread. In conjunction, the main application will just provide a menu to the user until the app is exited. Eventually the navigation thread will be put into a separate method so that it is periodically called every so often but this should not be relevant to this question, I don't think...
My understanding is that the separate thread should just run alongside the main console application and terminate when it has completed its task just like a console would if you don't prevent it exiting?????
What it actually looks like is that it is not starting in the first place as I get no response by way of the browser_DocumentCompleted event triggering (I know the IP address is alive and active, as I've checked!!)
Can anyone shed any light on why the separate thread is not running, or appears not to be?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
namespace ConsoleThreadTest
{
class Program
{
public delegate void Callback(string Status);
static void Main(string[] args)
{
NavigateToIPAddress GEIPA = new NavigateToIPAddress(new Uri("http://192.168.1.254"), new Callback(ResultCallback));
Thread PerformThreadTask = new Thread(new ThreadStart(GEIPA.PerformThreadTask));
PerformThreadTask.SetApartmentState(ApartmentState.STA);
PerformThreadTask.Start();
Console.WriteLine("{0}","Press escape key to exit");
while (true)
{
if (Console.KeyAvailable)
{
ConsoleKeyInfo key = Console.ReadKey(true);
switch (key.Key)
{
case ConsoleKey.Escape:
//Kill off thread if it is still running.
if (PerformThreadTask.ThreadState == ThreadState.Running)
{
PerformThreadTask.Abort();
}
Environment.Exit(0);
break;
default:
break;
}
}
}
}
public static void ResultCallback(string Status)
{
Console.WriteLine("{0}\t{1}", DateTime.Now.ToString("h:mm:ss"), Status);
}
public class NavigateToIPAddress
{
private Uri WebAddress;
private bool WebBrowserNavigationComplete = false;
// Delegate used to execute the callback method when the task is complete.
private Callback callback;
// The constructor obtains the state information and the callback delegate.
public NavigateToIPAddress(Uri IPAddressToNavigateTo, Callback callbackDelegate)
{
WebAddress = IPAddressToNavigateTo;
callback = callbackDelegate;
}
// The thread procedure performs the task and then invokes the callback delegate with the status.
public void PerformThreadTask()
{
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
try
{
br.Navigate(WebAddress);
}
catch (Exception e)
{
Console.WriteLine("{0}\tSome error occurred: {1}", DateTime.Now.ToString("h:mm:ss"), e.Message);
}
Application.Run();
while (WebBrowserNavigationComplete == false)
{
}
if (callback != null)
callback("Summit occurred");
}
private void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
var br = sender as WebBrowser;
if (br.Url == e.Url)
{
Console.WriteLine("{0}\tNavigated to {1}", DateTime.Now.ToString("h:mm:ss"), e.Url);
WebBrowserNavigationComplete = true;
}
}
}
}
}
Here I have a catch 22 situation. On one hand, if I leave the code as is above, anything after the application.run() method does not get executed which means the WebBrowserNavigationComplete flag will never change and the callback will never be returned.
If however I move application.run() after
if (callback != null)
callback("Summit occurred");
the code will never reach this point in order to call application.run() as it is stuck in the while loop waiting for the WebBrowserNavigationComplete flag which will never change as the message loop is never started!!
I cannot believe I am the first to do anything like this? What is the normal way to overcome this deadlock?
Thanks
WebBrowser is a winforms construct, and requires an application loop be set up to process messages for it. Since you have a console application and no message loop, it won't function properly.
You'll need to explicitly create a new application loop using Application.Run (which needs to be run from an STA thread) for it to work.

How to stop a running method with keyboard input in a Console Application on C#?

In short, I'm utilizing C# to scientific computation and I've written a method that has a while loop that may run to a user-specified quantity of steps... Actually, this method may take too long to execute (like more than 5 hours). When it takes this long, I may want to stop the method pressing Esc key, for example.
As I read something about breaking while, it is as simple as a Boolean flag or something like this. So I thought in something like this:
public Double? Run(int n)
{
int i = 0;
while ((i < n) && (/* inputkey != ConsoleKey.Escape */))
{
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
// I'm not worried about the return statement, as it is easy to do...
// returns null if the user skipped the method by pressing Escape
// returns null if the method didn't converged
// returns the double value that the method calculated otherwise
}
Well, this is what I wondered until now... So please, could you give useful ideas to this extent? How can I wait for a user input (I thought about Events, but I'm not sure how to implement it here and I think that it will make the code even slower, if I have to listen to a key at every while step the code goes into...
Well, any ideas or comments?
Update: I think I should have had described better the problem. All the solutions you gave me may solve this problem I proposed, but I think I was not completely reliable to my real problem. I don't know if I should ask another question or keep with this one...
You could run this method from a separate thread and set a stop variable when a key is pressed:
object myLock = new object();
bool stopProcessing = false;
public Double? Run(int n)
{
int i = 0;
while (i < n)
{
lock(myLock)
{
if(stopProcessing)
break;
}
// here goes the heavy computation thing
// and I need to read some "inputkey" as well to break this loop
i++;
}
}
and when a key is pressed, update stopProcessing accordingly:
Console.ReadKey();
lock(myLock)
{
stopProcessing = true;
}
If you're just wanting to stop the application, Ctrl-C from the command line will do it. If you really need to intercept input during a long running process, you might want to spawn a worker thread to do the long running process and then just use the main thread to interact with the console (i.e. Console.ReadLine()).
You will need to do this using threading. When you start the task, spawn a new thread and execute the task on that thread. Then in your Program.cs, wait for user input. If the user enters something meaningful - in your case, the Esc key - alert the background thread of the action. The simplest way to do this is by setting a static variable. The background thread will be checking this static variable and when it has been changed, the background thread will clean itself up and abort.
See the MSDN article on Threading.
A code sample will be a little more in depth, but it would look something like this:
public class Program.cs
{
public static myFlag = false;
public void Main()
{
thread = new Thread(new ThreadStart(DoWork));
thread.Start();
Console.ReadLine();
myFlag = true;
}
public static DoWork()
{
while(myFlag == false)
{
DoMoreWork();
}
CleanUp()
}
public static DoMoreWork() { }
public static CleanUp() { }
}
pool on Console.KeyAvailable in timely manner and take the action accordingly.
using System;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
class Program
{
static bool _cancelled = false;
static void Main( string[] args )
{
var computationTask = Task.Factory.StartNew(PerformIncredibleComputation);
var acceptCancelKey = Task.Factory.StartNew(AcceptCancel);
while (!acceptCancelKey.IsCompleted && ! computationTask.IsCompleted)
{
computationTask.Wait (100);
}
if( acceptCancelKey.IsCompleted && !computationTask.IsCompleted )
{
computationTask.Wait (new System.Threading.CancellationToken ());
}
else if(!acceptCancelKey.IsCompleted)
{
acceptCancelKey.Wait(new System.Threading.CancellationToken());
}
}
private static void PerformIncredibleComputation()
{
Console.WriteLine("Performing computation.");
int ticks = Environment.TickCount;
int diff = Environment.TickCount - ticks;
while (!_cancelled && diff < 10000)
{
//computing
}
Console.WriteLine("Computation finished");
}
private static void AcceptCancel()
{
var key = Console.ReadKey(true);
Console.WriteLine("Press Esc to cancel");
while(key.Key != ConsoleKey.Escape)
{
key = Console.ReadKey(true);
}
_cancelled = true;
Console.Write("Computation was cancelled");
}
}
}

How to start thread if button pressed and stop it if pressed again?

I'm using the next code to do what I'm asking for :
private delegate void CallerDelegate(object e);
CallerDelegate caler = new CallerDelegate(MethodToCall);
on button click event :
if (currBusyThrd != null && currBusyThrd.IsAlive)
{
currBusyThrd.Abort();
}
ThreadPool.SetMaxThreads(1, 1);
//queue the work for thread processing
ThreadPool.QueueUserWorkItem(new WaitCallback(WaitCallbackMethod))
"WaitCallbackMethod" Method is :
void WaitCallbackMethod(object stateInfo)
{
// argList : i put some argument in a list to use it in "MethodToCall" ...
BeginInvoke(caler,argList);
}
and the method i'm calling by the thread is :
void MethodToCall(object args)
{
//Here I get the thread I'm calling to stop it when btn clicked again
currBusyThrd = Thread.CurrentThread;
// The rest of the code ...
}
I feel that this is wrong ...
How to do it right ?
Actually the calling will be by TextBox_KeyUp .. so every time the user enter a char the code will execute again .. and the BackgroundWorker didn't work .
One problem to this approach is that it's very dangerous to arbitrarily Abort a thread (in pretty much any language). There are too many issues that can popup around unfreed resources and misheld locks. It's typically best to set some kind of flag to ask the Thread to safely abort itself or to forget about the thread and let it run to completion.
Additionally, Aborting a Thread in the ThreadPool is very dangerous and I believe not a supported operation. The Threads in the ThreadPool are not owned by you and Aborting them cold have serious implications for the ThreadPool.
Here is the solution I would take.
private object m_lock = new object();
private bool m_isRunning = false;
private bool m_isAbortRequested = false;
public void OnButtonClick(object sender, EventArgs e) {
lock ( m_lock ) {
if ( m_isRunning ) {
m_isAbortRequested = true;
} else {
m_isAbortRequested = false;
m_isRunning = true;
ThreadPool.QueueUserWorkItem(BackgroundMethod);
}
}
}
private void BackgroundMethod() {
try {
DoRealWork();
} finally {
lock (m_lock) {
m_isRunning = false;
}
}
}
private void DoRealWork() {
...
if ( m_isAbortRequested ) {
return;
}
}
Yes, this is very wrong. You should never try to manually control a ThreadPool thread. If you need this sort of control, you should be using your own Thread object. In addition, Abort() is not the recommended way of ending a thread; you should have a control volatile bool on your form that the code in MethodToCall checks at various points and exits gracefully when it's true. While you can use the same approach with the ThreadPool, the fact that you need to be able to cancel seems to indicate that the process is long-running, or at least has the potential to be. The ThreadPool shouldn't be used for long-running processes.
For example...
private volatile bool stopThread = false;
private Thread workThread;
private void StartThread()
{
if(workThread == null)
{
stopThread = false;
workThread = new Thread(new ThreadStart(MethodToCall));
workThread.Start();
}
}
private void StopThread()
{
if(workThread != null)
{
stopThread = true;
workThread.Join(); // This makes the code here pause until the Thread exits.
workThread = null;
}
}
Then in MethodToCall, just check the stopThread boolean at frequent intervals and do any cleanup work that you need to do and exit the method. For example...
private void MethodToCall()
{
// do some work here and get to a logical stopping point
if(stopThread)
{
// clean up your work
return;
}
// do some more work and get to another stopping point
if(stopThread)
{
// clean up your work
return;
}
}
And just repeat that pattern.
For situations where one thread needs to 'signal' another thread to do something, I usually use a System.Threading.ManualResetEvent to signal the secondary thread to stop, like this:
private volatile bool _threadRunning = false;
private ManualResetEvent _signal = new ManualResetEvent(false);
private Thread _thread;
private void OnButtonClick(object sender, EventArgs e)
{
if (!_threadRunning) {
// Reset the 'signal' event.
_signal.Reset();
// Build your thread parameter here.
object param = ;
// Create the thread.
_thread = new Thread(ExecuteThreadLogicConditionally(param));
// Make sure the thread shuts down automatically when UI closes
_thread.IsBackground = true;
// Start the thread.
_thread.Start();
// Prevent another thread from being started.
_threadRunning = true;
} else {
// Signal the thread to stop.
_signal.Set();
// DO NOT JOIN THE THREAD HERE! If the thread takes a while
// to exit, then your UI will be frozen until it does. Just
// set the signal and move on.
}
}
// If the thread is intended to execute its logic over and over until
// stopped, use this callback.
private void ExecuteThreadLogicUntilStopped(object param)
{
// Use a while loop to prevent the thread from exiting too early.
while (!_signal.WaitOne(0)) {
// Put your thread logic here...
}
// Set the flag so anther thread can be started.
_threadRunning = false;
}
// If the thread logic is to be executed once and then wait to be
// shutdown, use this callback.
private void ExecuteThreadLogicOnce(object param)
{
// Put your thread logic here...
//
// Now wait for signal to stop.
_signal.WaitOne();
// Set the flag so another thread can be started.
_threadRunning = false;
}
// If the thread needs to be stopped at any point along the way, use
// this callback. The key here is to 'sprinkle' checks of the 'signal'
// to see if the thread should stop prematurely.
private void ExecuteThreadLogicConditionally(object param)
{
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute small chunk of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Execute another small chuck of logic here...
if (_signal.WaitOne(0)) { _threadRunning = false; return; }
// Continue this pattern through the method.
}
Note that this solution does not use the ThreadPool at all. It could easily be made to do so. And as a suggestion, I wouldn't muck with SetMaxThreads() function on the ThreadPool. Just let the ThreadPool do its thing. It's been designed to be optimal for the way you use it.
Try this code..
using System;
using System.Linq;
using System.Windows.Forms;
using System.Threading;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Thread workerThread = null;
ManualResetEvent threadInterrupt = new ManualResetEvent(false);
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if (this.workerThread == null)
{
this.threadInterrupt.Reset();
this.workerThread = new Thread(() =>
{
int i = 0;
while (!this.threadInterrupt.WaitOne(0))
{
Debug.Print("put your code in here while worker thread running.. " + i.ToString());
Thread.Sleep(100);
i++;
}
this.workerThread = null;
// worker thread finished in here..
});
this.workerThread.IsBackground = true;
// start worker thread in here
this.workerThread.Start();
}
else
{
// stop worker thread in here
threadInterrupt.Set();
}
}
}
}

Categories