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
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);
}
}
}
I'm going through following method which is sending messages over Http.
private static void HTTPProcessQueue()
{
while (true)
{
try
{
Thread.Sleep(10000);
Utils.LogDebug("Msg Queue Check");
while (msgQueue.Count > 0)
{
QueueItem queueItem;
lock (msgQueue)
{
queueItem = msgQueue.Dequeue();
}
if (queueItem != null)
if(!HTTPTransmitEmailItem(queueItem.username, queueItem.filename))
Thread.Sleep(5000);
}
}
catch (Exception ex)
{
}
}
}
In the code above, why are Thread.Sleep(10000) and Thread.Sleep(5000) used in lines 7 and 18?
Also, why is there a while(true) in line 3?
As you requested, here is a slightly better way of doing it:
private static System.Collections.Concurrent.BlockingCollection<MsgType> msgQueue = new System.Collections.Concurrent.BlockingCollection<MsgType>();
private static void AddQueueItems() // simulate adding items to the queue
{
msgQueue.Add(new MsgType());
msgQueue.Add(new MsgType());
msgQueue.Add(new MsgType());
msgQueue.Add(new MsgType());
// when adding is done, or the program is shutting down
msgQueue.CompleteAdding();
}
private static void HTTPProcessQueue()
{
foreach (var queueItem in msgQueue.GetConsumingEnumerable())
{
if (queueItem != null)
{
if (!HTTPTransmitEmailItem(queueItem.username, queueItem.filename))
{
Thread.Sleep(5000);
}
}
}
}
I'd recommending using the async/await pattern with HTTPTransmitEmailItem, and then you can use await Task.Delay(...) instead of Thread.Sleep(...). I've also not included any error checking in this code.
This would then look more like:
private static async Task HTTPProcessQueue()
{
foreach (var queueItem in msgQueue.GetConsumingEnumerable())
{
if (queueItem != null)
{
if (!(await HTTPTransmitEmailItemAsync(queueItem.username, queueItem.filename)))
{
await Task.Delay(5000);
}
}
}
}
But you would have to make a HttpTransmitEmailItemAsync method. Also note that the GetConsumingEnumerable(...) method has an overload which takes a CancellationToken, so you could use this to gain more control over when to end the queue process. You can learn about async/await here.
The Thread.Sleep(10000) is used on line 7 to let the system pause / wait for 10 seconds before it starts the function Utils.LogDebug("Msg Queue Check"); to log the debug information with message "Msg Queue Check". and i believe the Thread.Sleep(5000) is added at the end of loop to create a delay or to wait for 5 seconds before process the next loop.
while(true) is usually used for infinite loop. all method inside this loop will run in loop in infinite time.
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());
I'm using parallel.for to launch in many threads a external program. But despite the fact that these are separate threads I need implement sth like delay. E.g. 2 threads want to launch this external program at the same moment - then one of them should wait and start e.g. 10 sec after second thread.
Is it possible?
It's possible, but given the information you've provided it seems pointless... you're enforcing single-threaded execution of the external program, so you might as well have a single thread executing it. If Thread 2 has to wait for Thread 1 in order to start the "external program," then just let Thread 1 do all the work since it already knows when it started the "external program."
The only benefit you will get from a multi-threaded approach is if you have a bunch of processing that you need to do prior to executing the "external program" and that processing has to be a good candidate for concurrent execution.
Update
OK, there are a couple of ways to do this with only one extra thread in order to keep your Main/GUI thread responsive. The first approach is a simple lock around the external resource which you're interacting with:
public class ExternalResourceHandler
{
private readonly ExternalResource _resource;
private readonly object _sync = new object();
// constructors
// ...
// other methods
public void PerformExternalOperation()
{
lock(_sync)
{
Result result = _resource.Execute();
// do soemthing with the result
}
}
}
Here are 3 multi-threaded version for executing the code:
Using a Parallel.For method: recommended if the external program takes a short amount of time to execute- I'd suggest for things under 25 seconds (although this is not necessarily a "correct" number).
Using a ThreadPool: again, I'd recommend for things that take less than 25 seconds (with the same reservation as above).
Using a Thread: this would be recommended if the operation runs longer (i.e. more than 25 seconds, but it would do just as good if it's under 25 seconds).
Here are some examples (not necessarily functional, mostly meant to give you an idea of the different approaches):
public class Program
{
public static ExternalResourceHandler _erh = new ExternalResourceHandler();
static int Main()
{
Console.WriteLine("Type 'exit' to stop; 'parallel', 'pool' or 'thread' for the corresponding execution version.");
string input = Console.ReadLine();
while(input != "exit")
{
switch(input)
{
case "parallel":
// Run the Parallel.For version
ParallelForVersion();
break;
caase "pool":
// Run the threadpool version
ThreadPoolVersion();
break;
case "thread":
// Run the thread version
ThreadVersion();
break;
default:
break;
}
input = Console.ReadLine();
}
return 0;
}
public static void ParallelForVersion()
{
Parallel.For(0, 1, i =>
{
_erh.PerformExternalOperation();
});
}
public static void ThreadPoolVersion()
{
ThreadPool.QueueUserWorkItem(o=>
{
_erh.PerformExternalOperation();
});
}
public static void ThreadVersion()
{
Thread t = new Thread(()=>
{
_erh.PerformExternalOperation();
});
t.IsBackground = true;
t.Start();
}
}
The other option is to employ the Producer/Consumer design pattern where your ExternalResourceHandler is the consumer and it processes requests to the external resource from a thread-safe queue. Your main thread just places requests on the queue and immediately returns back to work. Here is an example:
public class ExternalResourceHandler
{
private volatile boolean _running;
private readonly ExternalResource _resource;
private readonly BlockingQueue<Request> _requestQueue;
public ExternalResourceHandler( BlockingQueue<Request> requestQueue)
{
_requestQueue = requestQueue;
_running = false;
}
public void QueueRequest(Request request)
{
_requestQueue.Enqueue(request);
}
public void Run()
{
_running = true;
while(_running)
{
Request request = null;
if(_requestQueue.TryDequeue(ref request) && request!=null)
{
_resource.Execute(request);
}
}
}
// methods to stop the handler (i.e. set the _running flag to false)
}
Your main would look like this:
public class Program
{
public static ExternalResourceHandler _erh = new ExternalResourceHandler();
static int Main()
{
Thread erhThread = new Thread(()=>{_erh.Run();});
erhThread.IsBackground = true;
erhThread.Start();
Console.WriteLine("Type 'exit' to stop or press enter to enqueue another request.");
string input = Console.ReadLine();
while(input != "exit")
{
_erh.EnqeueRequest(new Request());
input = Console.ReadLine();
}
// Stops the erh by setting the running flag to false
_erh.Stop();
// You may also need to interrupt the thread in order to
// get it out of a blocking state prior to calling Join()
erhThread.Join();
return 0;
}
}
As you see: in both cases all the work for the external handler is forced on a single thread yet your main thread still remains responsive.
Look at producer-consumer pattern. The first thread produces the information "external progam launched" the second thread consumes it, waits 10 second and then launches the external program.
I have the following method:
public void PutFile(string ID, Stream content)
{
try
{
ThreadPool.QueueUserWorkItem(o => putFileWorker(ID, content));
}
catch (Exception ex)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = ex });
}
}
The putFileWorker method looks like this:
private void putFileWorker(string ID, Stream content)
{
//Get bucket name:
var bucketName = getBucketName(ID)
.ToLower();
//get file key
var fileKey = getFileKey(ID);
try
{
//if the bucket doesn't exist, create it
if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client))
s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU });
PutObjectRequest request = new PutObjectRequest();
request.WithBucketName(bucketName)
.WithKey(fileKey)
.WithInputStream(content);
S3Response response = s3client.PutObject(request);
var xx = response.Headers;
OnPutFileCompleted(this, new ValueEventArgs { Value = ID });
}
catch (Exception e)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = e });
}
}
I've created a little console app to test this.
I wire up event handlers for the OnPutFileError and OnPutFileCompleted events.
If I call my PutFile method, and step into this, it gets to the "//if the bucket doesn't exist, create it" line, then exits. No exception, no errors, nothing.
It doesn't complete (i've set breakpoints on my event handlers too) - it just exits.
If I run the same method without the ThreadPool.QueueUserWorkItem then it runs fine...
Am I missing something?
ThreadPool threads are background threads (see the link). They will not keep your application running if the main thread exits.
Typically, in WinForms apps, this is not a problem, because the main UI thread calls Application.Run and starts processing events. For your console app, if your Main method doesn't wait for the work item to complete somehow, the main thread will queue the work item and then exit.
You could create a background thread yourself and set its IsBackground property to false. Or you could create a thread and call Thread.Join to wait for it to finish.
-- EDIT --
As suggested in the comments below, you could also use a ManualResetEvent, or even a custom synchronization class as suggested by Linik. The goal is to block the main thread until the the background threads have completed.
To use a ManualResetEvent, create it in your main thread and pass it in as an argument. (I'll assign it to a static variable here just for brevity.)
ManualResetEvent s_WaitEvent;
ManualResetEvent s_WaitEvent = new ManualResetEvent(false); // non-signaled
// queue work item here
s_WaitEvent.WaitOne();
At the end of your worker thread, signal the event:
s_WaitEvent.Set();
Link's CountDownLatch is nice if you have many threads that must process before you can exit. You can also use separate ManualResetEvents for each thread and wait for them all to complete using WaitHandle.WaitAll(WaitHandle[]). (ManualResetEvent inherits from WaitHandle.)
Put a Console.ReadLine() in your Main thread to block it while you test your worker thread. This will keep main from exiting. Just hit enter when you're done.
Use a CountDownLatch to force the main to wait for all of the threads that you have queued up:
public class CountDownLatch
{
private int m_remain;
private EventWaitHandle m_event;
public CountDownLatch (int count)
{
if (count < 0)
throw new ArgumentOutOfRangeException();
m_remain = count;
m_event = new ManualResetEvent(false);
if (m_remain == 0)
{
m_event.Set();
}
}
public void Signal()
{
// The last thread to signal also sets the event.
if (Interlocked.Decrement(ref m_remain) == 0)
m_event.Set();
}
public void Wait()
{
m_event.WaitOne();
}
}
In Main:
static void Main(string[] args)
{
CountDownLatch latch = new CountDownLatch(numFiles);
//
// ...
//
putFileWorker("blah", streamContent);
//
// ...
//
// waits for all of the threads to signal
latch.Wait();
}
In the worker method:
private void putFileWorker(string ID, Stream content)
{
try
{
//Get bucket name:
var bucketName = getBucketName(ID)
.ToLower();
//get file key
var fileKey = getFileKey(ID);
try
{
//if the bucket doesn't exist, create it
if (!Amazon.S3.Util.AmazonS3Util.DoesS3BucketExist(bucketName, s3client))
s3client.PutBucket(new PutBucketRequest { BucketName = bucketName, BucketRegion = S3Region.EU });
//
// ...
//
}
catch (Exception e)
{
OnPutFileError(this, new ExceptionEventArgs { Exception = e });
}
}
finally
{
latch.Signal();
}
}