5

I'm having a problem using Console.ReadKey() with Thread.Sleep(). I wrote the following method as a replacement for Console.Readline() with the added functionality of being able to be interrupted.

public static string Readline()
{
    Interrupted = false;
    ConsoleKeyInfo keyInfo;
    string input = "";
    do
    {
        while(!Console.KeyAvailable)
        {
            Thread.Sleep(20);

            if(Interrupted)
            {
                break;
            }
        }

        if(Interrupted)
        {
            break;
        }

        keyInfo = Console.ReadKey(true);
        if(keyInfo.Key == ConsoleKey.Backspace)
        {
            if(input.Length > 0)
            {
                input = input.Remove(input.Length - 1, 1);
                Console.Write("\b \b");
            }
        }
        else
        {
            input += keyInfo.KeyChar;
            Console.Write(keyInfo.KeyChar);
        }
    }
    while(keyInfo.Key != ConsoleKey.Enter);
    Console.WriteLine();

    return input;
}

The problem here is that calling Thread.Sleep() causes Console.ReadKey() to echo the key pressed, even though true is passed in to Console.ReadKey() to disable echoing. The key pressed is supposed to be echoed by the Console.Write() call further down, so what actually happens when the program runs is that each key echoes twice. When I remove the call to Thread.Sleep(), everything works as it should, however CPU usage shoots up to 100% while it's waiting for Console.KeyAvailable.

Here's what I've already tried:

  • Removing the while(!Console.KeyAvailable) and placing all the code in the loop, except the Thread.Sleep() and the interrupt check, inside of an if(Console.KeyAvailable)

  • Adding another Thread.Sleep() right before and after the call to Console.ReadKey()

  • Removing the Console.Write() call that echoes the pressed key. Each key does only echo once, however backspace no longer works.

  • Placing the interrupt checking loop containing the Thread.Sleep() in a separate thread and waiting on an AutoResetEvent in the main loop containing the Console.ReadKey()

  • Running the method asynchronously and using await Task.Delay() in place of Thread.Sleep()

None of these have fixed the issue. Is there anything I could do to make Console.ReadKey() work as intended, such as an alternative to Console.ReadKey() or Thread.Sleep()? To put it simply, what I need is something that can limit CPU usage while waiting without causing Console.ReadKey() to echo keypresses when it shouldn't.

  • What are you trying to actually do with this code? – xxbbcc Apr 24 '18 at 23:17
  • @xxbbcc The purpose of the method is to emulate the functionality of `Console.Readline()`, but to allow other threads to interrupt it while it's waiting for input. – James Dumas Apr 24 '18 at 23:22
  • 1
    Like what? If a thread puts something into the console, it may interfere with what users type. Why don't you flip this and just use `Console.ReadLine()` and do your work on background threads? What do you gain by emulating console functionality? (Old DOS programs that didn't use an interrupt were running at 100% CPU for this same reason.) – xxbbcc Apr 24 '18 at 23:24
  • Possibly related: [Strange behaviour of Console.ReadKey with multithreading](https://stackoverflow.com/q/15143931/1220550) – Peter B Apr 24 '18 at 23:29
  • @xxbbcc The problem with that is that, as far as I know, there is no way to interrupt `Console.Readline()`, it will run until enter is pressed. If I need the program to stop recieving input, `Console.Readline()` won't allow for that. – James Dumas Apr 24 '18 at 23:30
  • 2
    I pasted your method into a test project here, and I am unable to repro your issue with `Sleep()`. With or without it, I see each character echoed one time as expected. – glenebob Apr 24 '18 at 23:39
  • Look at this answer then: https://stackoverflow.com/a/12221829/682404 – xxbbcc Apr 24 '18 at 23:40
  • Also, the interrupt logic is broken. If `Interrupted` becomes true, program execution ends up at `Console.ReadKey(true)`, which blocks if there's no key in the buffer. I assume `Interrupted` is intended to cause the method to immediately return whatever input has been received so far? – glenebob Apr 24 '18 at 23:49
  • Also also, strings are immutable, so the += operator is not the best approach for concatenation. You should use a StringBuilder to receive input characters. – glenebob Apr 24 '18 at 23:53
  • @glenebob Thanks for pointing that out, I edited the question to fix it. I had it correct in my code but I accidentally deleted it while messing around with it. – James Dumas Apr 24 '18 at 23:53

0 Answers0