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

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.

Related

How to use console command to quit infinite loop in C#

So I have an infinite loop that runs some code and I want it to run forever until a user types an exit command. However the problem I am having is when the code reaches the ReadLine command, it stays there until it receives a valid input from the console. How can I code it so it will move on to the rest of the code and only read from the console if there is a valid input
while(true)
{
uint quit = Uint32.Parse(Console.ReadLine());
if(quit == 1){
break;
}
else{
//some other code
}
}
You will need to use an Asynchronous library which monitors the console for keystrokes and will pipe out commands.
I would recommend becoming familiar with IAsyncEnumerable
The pattern usually is to implement some form of IAsyncEnumerable<YourApplicationEvent> service.
Whenever you need to pipe out to the console you can have the async service yield return a YourApplicationEvent, while also piping out a YourApplicationEvent whenever the user hits enter to send command.
I use a method like this usually for this type of task:
public async IAsyncEnumerable<string> ConsoleEvents([EnumeratorCancellation] CancellationToken token)
{
await using var reader = Console.OpenStandardInput();
var commandBuffer = new List<byte>();
while (!token.IsCancellationRequested)
{
var buffer = new byte[4096];
var readCount = await reader.ReadAsync(buffer, token);
var command = new string(Encoding.UTF8.GetChars(buffer, 0, readCount));
yield return command.TrimEnd();
}
}
You're going to want to look into asynchronous programming.
Essentially, you're going to want to create a Task that takes care of running your code in the background, and you can wait for some console input to cancel the task if needed.
Typically this is achieved by implementing a Cancellation Token, which you can update from your loop that is waiting for user input.
Here is an example app I just made that should show you how this is accomplished:
class Program
{
static void Main(string[] args)
{
// create our token source here
var cancellationTokenSource = new CancellationTokenSource();
// and get the actual cancellation token from the token source
var cancellationToken = cancellationTokenSource.Token;
// have the Task library run an async task for us, and pass in our cancellation Token
Task.Run(
async () => {
// while our task is not cancelled
while (!cancellationToken.IsCancellationRequested)
{
// perform our code
Console.WriteLine("Simulated background code.");
await Task.Delay(1000);
}
}, cancellationToken);
// say hi
Console.WriteLine("Hello World!");
// while we havent requested a cancellation we loop
while (!cancellationToken.IsCancellationRequested)
{
// wait on reading a line
var escape = Console.ReadLine();
// if that line is quit we cancel the token
if (escape == "quit")
cancellationTokenSource.Cancel();
}
Console.WriteLine("See you later.");
}
}
}

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);
}
}
}

Give code limited amount of time to execute within

I have a line of code in an app that looks like this:
string botMessage = chatterBotSession.Think(input);
It's querying a chat bot service and stores the response in botMessage. Sometimes however, the chat bot can take too long to think of a response.
Is there a way I can run the code like normal, but if it doesn't complete in one second cancel it and then run some handler that can alert the user that the service took too long?
Like, normally I might do this:
string botMessage = chatterBotSession.Think(input);
Console.WriteLine("The bot responded with: " + botMessage);
but if the bot is slow the second line doesn't get executed (fast enough). How can I limit the bot's "thinking" time to one second, and run the rest of the code (that would normally run) as soon as it completes if it was successful or run a separate bit of code to display an error message if it hasn't completed.
You can wrap the bot's service call in a Task.Run call and wait for it a predefined amount of time. It would look something like this
static void Main(string[] args)
{
var task = Task.Run(() => chatterBotSession.Think(input));
if (task.Wait(1000))
{
Console.WriteLine(task.Result);
}
else
{
Console.WriteLine("Couldn't get an answer in a timely manner");
}
Console.ReadLine();
}
It is very easy to limit the task execution by using CancellationTokenSource with timeout:
var cancellationToken = new CancellationTokenSource();
var task = chatterBotSession.Think(cancellationToken.Token);
cancellationToken.CancelAfter(TimeSpan.FromMilliseconds(1000)); // cancel after 1sec
await task;
In Think method you should add call to CancellationToken.ThrowIfCancellationRequested
Calling Bot:
static void Main(string[] args)
{
TalkingBot bot = new TalkingBot();
try
{
Console.WriteLine("Thinking started...");
Console.WriteLine(bot.Think("Some input...", 2000));
}
catch (Exception ex)
{
Console.WriteLine("Error: {0}", ex.Message);
}
Console.ReadLine();
}
And Bot itself:
class TalkingBot
{
string input = null;
int timeout = 0;
string asyncThnikResult = null;
public string Think(string input, int timeout)
{
DateTime timeLimit = DateTime.Now.AddMilliseconds(timeout);
this.input = input;
this.timeout = timeout;
System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(AsyncThnik));
thread.Start();
//wait for result, in this case
while (string.IsNullOrEmpty(asyncThnikResult))
{
if (timeLimit <= DateTime.Now)
{
throw new Exception("Timeout occured!");
}
System.Threading.Thread.Sleep(10);
}
//return result...
return this.asyncThnikResult;
}
/// <summary>
/// Do your thing async...
/// </summary>
void AsyncThnik()
{
string temp = "This value will never be returned due to timeout limit...";
System.Threading.Thread.Sleep(timeout + 1000); //add second to produce timeout...
this.asyncThnikResult = temp;
}
}

C# Tweetinvi Library - how to stop stream

Attempting to get a filtered stream using the Tweetinvi library via c#.
I am trying to do something similar to the search, which I have been able to successfully receive tweets:
public List<ITweet> SearchTweets(string query)
{
List<ITweet> tweets = new List<ITweet>();
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var searchParam = Search.GenerateSearchTweetParameter(query);
searchParam.Lang = Language.English;
searchParam.MaximumNumberOfResults = 100;
tweets = Search.SearchTweets(searchParam);
});
return tweets;
}
but the limitation here is that I can only get a max 100 tweets. I want to leverage the stream in a similar way and get a stream of tweets matching the tracks that I pass. I have tried this:
public void FilterStream(string query)
{
IFilteredStream tweets;
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var filteredStream = Tweetinvi.Stream.CreateFilteredStream();
filteredStream.AddTrack(query);
filteredStream.AddTrack("#" + query);
filteredStream.MatchingTweetReceived += (sender, args) => { Debug.WriteLine(args.Tweet.Text); };
filteredStream.StartStreamMatchingAllConditions();
});
}
but problem is it seems to run in an infinite loop and i'm unsure where to stop or limit the number of tweets I received from the stream to make it stop. The library's documentation is quite unclear and I have been unable to achieve the behavior I am seeking. I'm sure I am on the right route, just unsure how to stop the stream and store all the tweets I've received in a List<ITweet> construct.
To stop the stream use:
filteredStream.StopStream();
You can keep a count of the tweets globally then stop when the number of tweets you need is received.
I had the same problem and ended up doing this:
var task = filteredStream.StartStreamMatchingAllConditionsAsync();
while (true)
{
Thread.Sleep(500);
if (task.IsCanceled || task.IsCompleted || task.IsFaulted)
{
break;
}
if (ShouldStop)
{
filteredStream.StopStream();
break;
}
}
task.Wait();
StartStreamMatchingAllConditionsAsync() will start the task and return immediately, then the endless while loop will check for a condition to stop the stream and break the loop.
ShouldStop is a static boolean variable that is flipped to True when the code should stop streaming from twitter.
Then we Wait() for the stream task to finish.
That code smells but it seems to work! I'd be curious to see if someone comes up with a better implementation...
You should try to put a condition inside the MatchingTweetReceived method like this :
public void FilterStream(string query)
{
IFilteredStream tweets;
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var filteredStream = Tweetinvi.Stream.CreateFilteredStream();
filteredStream.AddTrack(query);
filteredStream.AddTrack("#" + query);
filteredStream.MatchingTweetReceived += (sender, args) => {
Debug.WriteLine(args.Tweet.Text);
if (condition)
stream.StopStream();
};
filteredStream.StartStreamMatchingAllConditions();
});
}
This worked fine for me.
I know it's an old question, but I've just done this:
public void Start()
{
_stream = Stream.CreateFilteredStream(_creds);
// Do whatever stream setup you want to do
Task.Factory.StartNew(() => {
Debug.WriteLine("Thread started");
_stream.StartStreamMatchingAllConditions();
Debug.WriteLine("Thread stopped");
});
}
public void Stop()
{
_stream.StopStream();
}

asynchronous console application or Service?

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());

Categories

Resources