asynchronous console application or Service? - c#

I'm trying to create a C# .net 4.5 console application that will run a loop continuously unless passed a command from the user. I don't want to check for an input from the user each time the loop completes because I expect this to run at least hours without user input, maybe even longer, however they should be able to type a command into the console at any time to be evaluated for how the loop should behave.
To allow for user input at the same time the loop is running I believe I need to use threading such as async-await, however I'm quite new to programming and I'm not sure if threading is even the right approach. I've also recently learned of Services and since the user input is mostly stop and start, this may be the better approach.
How should I modify my code below to implement asynchronous functionality allowing user input while running a continuous loop? Should this be a service instead as most user input is start and stop?
I've included the code I've written so far:
class Program
{
static void Main(string[] args)
{
OptionMenu();
Console.ReadLine();
}
static void OptionMenu()
{
string command = "";
Console.WriteLine("Please enter a command: \n");
while (command != "exit")
{
command = Console.ReadLine().ToLower();
switch (command)
{
case "exit":
command = "exit";
break;
case "pause":
command = "Pause";
break;
case "start":
while (command != "Pause")
{
PrimaryWork();
}
break;
default:
Console.WriteLine("I'm sorry Dave, I'm afraid I can't do that");
break;
}
}
}
static void PrimaryWork()
{
Console.WriteLine("Results of PrimaryWork should be written to a repository");
Thread.Sleep(5000);
}
}

Here's a pattern that might help you:
void Main()
{
var tokenSource = new CancellationTokenSource();
var t=new Thread(_ => DoWork(tokenSource.Token));
for(;;) //loop forever
{
var input = Console.ReadLine();
if(input == "exit")
{
tokenSource.Cancel();
break;
}
}
}
void DoWork(CancellationToken token)
{
for(;;)
{
//do some work
if(token.IsCancellationRequested)
{
break;
}
}
}

It is unlikely using async/await will make your code easier in this case.
Consider creating separate thread that deals with "primary work" and have main thread posting command to ConcurrentQueue which will be checked by the other thread.
Starting point Thread.Start:
var commands = new ConcurrentQueue<string>();
var myThread = new Thread(_ =>
{
while(true)
{
// Do work
...
// check commands
string command;
if (commands.TryDequeue(out command))
{
//handle command
}
}
});
myThread.Start();
commands.Enqueue(Console.ReadLine());

Related

c# - can i run code while requesting ReadLine from the user?

ive got a loop that runs in the background. at the same time I need the program to register inputs by the user, and have the code react if any input happens. can I somehow do this?
So from a snippet like this-
string input = "";
while(true)
{
Console.WriteLine(input);
}
input = PermanentReadLine();
id expect it to change what it writes to the screen whenever i write anything into the ReadLine
You can accomplish this by having one task reading input and a separate one processing it. Here's an implementation using Channels:
async Task Main()
{
var channel = Channel.CreateUnbounded<string>();
var worker = Task.Run(async () =>
{
while (await channel.Reader.WaitToReadAsync())
while (channel.Reader.TryRead(out var input))
Console.WriteLine(input);
});
while (true)
{
var input = Console.ReadLine();
if (input.Equals("exit", StringComparison.CurrentCultureIgnoreCase))
{
channel.Writer.Complete();
break;
}
channel.Writer.TryWrite(input);
}
}
This example terminates when the user types "exit".
Task.Run(() => {
while(true)
{
string input = Console.ReadLine();
// Do something with the input. Maybe switch or if/else statement?
}
});
This will still allow the rest of the program to continue.

How to Console.ReadLine from one thread and consume ConsoleKeys from another thread?

I'm making a test console application. This application runs a void task (I can't change this fact), and for it to remain opened I insert Console.ReadLine at end of Main method.
Is there any way to consume each key being pressed from other threads? I tried the following but call to Peek is blocking the thread.
loop = Task.Run(async () =>
{
var input = Console.In;
while (running)
{
int key = input.Peek(); // blocks here forever
if (key == -1)
{
await Task.Delay(50);
}
else
{
input.Read();
if ((ConsoleKey)key == ConsoleKey.Enter)
{
Completed?.Invoke();
}
else
{
OnKeyDown((ConsoleKey)key);
}
// todo how to intercept keyup?
}
}
});
This is the main method
static void Main(string[] args)
{
GrpcEnvironment.SetLogger(new Grpc.Core.Logging.ConsoleLogger());
//setup MagicOnion and option.
var service = MagicOnionEngine.BuildServerServiceDefinition(isReturnExceptionStackTraceInErrorDetail: true);
var server = new global::Grpc.Core.Server
{
Services = { service },
Ports = { new ServerPort("localhost", 12345, ServerCredentials.Insecure) }
};
// launch gRPC Server.
server.Start();
// and wait.
Console.ReadLine();
}
what I want is basically to have a keyboard key pressed event listener on another thread.
I also tried global keyboard hooks but that does not work for console application.
I decided to put this instead of Console.ReadLine at end of Main method.
while (true) Task.Delay(1000).Wait(); // console.ReadLine doesn't let us to read from console in other threads.
And then I can do
loop = Task.Run(() =>
{
while (running)
{
var key = Console.ReadKey(true).Key;
if (key == ConsoleKey.Enter)
{
Completed?.Invoke();
}
else
{
OnKeyDown(key);
}
// todo how to intercept keyup?
}
});
by pressing enter, our application wont close but this is a test application and exit with enter is not our requirement.
but If someone still knows an anwer with Console.ReadLine I appreciate to know it.
You consider just trying something like this?
Make sure to try running this from an actual console as my mileage with VS 2017 varied on CTRL-C working in the IDE. (I should have mentioned this uses C# 7.2 - for async main)
class Program
{
static async Task Main()
{
CancellationTokenSource cts = new CancellationTokenSource();
Console.CancelKeyPress += (sender, args) => cts.Cancel();
Console.WriteLine("Press CTRL-C to Exit");
// Start you server here
while (!cts.IsCancellationRequested)
{
if (Console.KeyAvailable)
{
var key = Console.ReadKey(true);
Console.WriteLine($"Read: {key.KeyChar}");
}
await Task.Delay(50, cts.Token);
}
}
}

How to have a c# console application wait (synchronously) for user input (ReadKey/ReadLine) for a duration?

I have a console application that runs automated procedures on a server. However, there are routines that may require user input. Is there a way to have the console wait for user input for a set amount of time? If there is no user input, proceed with execution, but if there is input, then process accordingly.
This is suprisingly difficult: You have to start a new thread, do the ReadLine on this new thread, on the main thread wait with timeout for the new thread to finish, if not abort it.
That was quite a tough one! However I'm bored and like a challenge :D Try this out...
class Program
{
private static DateTime userInputTimeout;
static void Main(string[] args)
{
userInputTimeout = DateTime.Now.AddSeconds(30); // users have 30 seconds before automated procedures begin
Thread userInputThread = new Thread(new ThreadStart(DoUserInput));
userInputThread.Start();
while (DateTime.Now < userInputTimeout)
Thread.Sleep(500);
userInputThread.Abort();
userInputThread.Join();
DoAutomatedProcedures();
}
private static void DoUserInput()
{
try
{
Console.WriteLine("User input ends at " + userInputTimeout.ToString());
Console.WriteLine("Type a command and press return to execute");
string command = string.Empty;
while ((command = Console.ReadLine()) != string.Empty)
ProcessUserCommand(command);
Console.WriteLine("User input ended");
}
catch (ThreadAbortException)
{
}
}
private static void ProcessUserCommand(string command)
{
Console.WriteLine(string.Format("Executing command '{0}'", command));
}
private static void DoAutomatedProcedures()
{
Console.WriteLine("Starting automated procedures");
//TODO: enter automated code in here
}
}

Using a Thread Multiple Times

I'm trying to use a thread multiple times and have the thread stop processing if the user doesn't react fast enough.
Thread ask = new Thread (new ThreadStart (MathQuiz.prompt));
ask.Start();
ask.Join(30000);
if (answer == 4)
{
score = score+1;
answer = 0;
}
Console.WriteLine();
Console.WriteLine("Press any key to move on to the next question!");
Console.WriteLine();
Console.ReadKey(true);
Console.WriteLine("What is 15 / 3?");
Console.WriteLine();
ask.Start();
ask.Join(30000);
if (answer == 5)
{
score = score+1;
answer = 0;
}
...
static void prompt()
{
preanswer = (Console.ReadLine());
if (!decimal.TryParse(preanswer, out answer))
{
Console.WriteLine("That wasn't even a number or decimal!");
}
else
{
answer = decimal.Parse(preanswer);
}
}
Right now it doesn't seem that the "prompt" thread is terminating, and so it crashes when the second question begins.
So I need a solution! I of course don't mind to answer questions to help myself get an answer.
The method Thread.Join(Int32) doesn't stop the other thread after the given number of milliseconds. It just stops waiting. It returns true if the other thread has terminated.
So if ask.Join(30000); returns false, the other thread is still running and you have to Abort the thread yourself.
What evidence do you have that the prompt thread is not terminating? The main thread should be waiting for the prompt thread to terminate at the "join" places, so, if execution continues past a join, the "prompt" thread terminated.
Take a look at the example on MSDN's Thread.Join() page. The example uses two different methods for passing work to a thread. regularThread is what you are trying to do. For the simple task in your example, I think doing a join() without events or locks is a reasonable solution. If you are prototyping to make a product that is much more robust than your example implies, then you should also: 1) read up on the ThreadPool . It avoids the cost of creating/disposing threads. 2) Put a lock() block around reads and writes to the answer variable.
A word of warning: Other answers mention using Thread.Abort(). Calling Thread.Abort() on the currently executing thread is fine and nearly equivalent of throwing an exception. However calling Thread.Abort() on a different thread should be avoided. There's several scenarios where it can result in the thread not cleaning up properly.
Console.ReadLine blocks the current thread from being aborted until the line has been read. (As per comment from Timwi)
To work around this, you would have to use the Console.KeyAvailable method here: How to add a Timeout to Console.ReadLine()?
I re-wrote the problem before realising my mistake, and now here's a solution.
It's rather more involved than I wanted it to be. (Using the KeyAvailable means I need to queue up the keys entered, and to support backspace I have to remove the items. I also need to sleep while no keys are available... )
private static AutoResetEvent answered = new AutoResetEvent(false);
private static Func<string, bool> questionCorrect = null;
private static bool? correct;
static void Main(string[] args)
{
int score = 0;
AskQuestion(ref score,
"What is 15 / 3?",
TimeSpan.FromSeconds(5),
answer =>
{
decimal value;
if (!decimal.TryParse(answer, out value))
{
Console.WriteLine(
"That was not a valid number");
return false;
}
return (value == 15/3);
});
AskQuestion(ref score,
"What is 20 * 2 ?",
TimeSpan.FromSeconds(5),
answer =>
{
decimal value;
if (
!decimal.TryParse(answer,
out value))
{
Console.WriteLine(
"That was not a valid number");
return false;
}
return (value == 20*2);
});
Console.WriteLine("Done. Score: {0}", score);
Console.ReadLine();
}
private static void AskQuestion(ref int score, string question, TimeSpan duration, Func<string, bool> validator)
{
// Setup
questionCorrect = validator;
correct = null;
answered.Reset();
// Ask
Console.WriteLine(question);
Thread thread = new Thread(GetQuestion);
thread.Start();
// Wait
answered.WaitOne(duration);
thread.Abort();
thread.Join();
Console.WriteLine(); // Write empty line, otherwise this overwrites the answer.
// Validate);
if (correct.HasValue && correct.Value == true)
{
score++;
Console.WriteLine("Correct");
}
else if (correct.HasValue)
{
Console.WriteLine("Incorrect");
}
else
{
Console.WriteLine("Timeout");
}
}
private static void GetQuestion()
{
try
{
List<char> captured = new List<char>();
bool answerCaptured = false;
while (!answerCaptured)
{
while (Console.KeyAvailable)
{
var key = Console.ReadKey();
if (key.KeyChar == '\r' || key.KeyChar == '\n')
{
answerCaptured = true;
break;
}
if (key.KeyChar == '\b' && captured.Count > 0)
{
captured.RemoveAt(captured.Count - 1);
}
else
{
captured.Add(key.KeyChar);
}
}
Thread.Sleep(50);
}
string answer = new string(captured.ToArray());
correct = questionCorrect.Invoke(answer);
answered.Set();
}
catch (ThreadAbortException)
{
// will be thrown when the thread times out.
}
}

c# reading user input without stopping an app

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

Categories

Resources