Console ReadLineAsync is actually blocking when it should be returning a Task immediately - by ZoomZoomZoom

Status : 

  Fixed<br /><br />
		This item has been fixed in the current or upcoming version of this product.<br /><br />
		A more detailed explanation for the resolution of this particular item may have been provided in the comments section.

Sign in
to vote
ID 779079 Comments
Status Closed Workarounds
Type Bug Repros 1
Opened 2/12/2013 2:43:11 PM
Access Restriction Public


These two links more than adequately explain the problem
Sign in to post a comment.
Posted by ohnobinki on 4/4/2016 at 3:23 PM
Using code like:

var l1 = Task.Run(() => Console.ReadLine());
var l2 = Task.Run(() => Console.ReadLine());

Console.WriteLine($"1. {l1}");
Console.WriteLine($"2. {l2}");

is inherently broken because it isn’t guaranteed which task the threadpool will pick up first. However, as long as your calls to Console.In.ReadLineAsync() are all performed on the same thread, the API could/should use a queue to ensure the resultant tasks are fulfilled in first-come first-served/FIFO order. It may take work to implement such a thing. But my example wrapper which uses a queue to enforce ordering for synchronized calls looks like:

class AsyncTextReaderWrapper
    TextReader Reader { get; }
    readonly object lastLock = new object();
    Task Last { get; set; } = Task.CompletedTask;

    public AsyncTextReaderWrapper(
        TextReader reader)
        Reader = reader;

    public Task<string> ReadLineAsync()
        lock (lastLock)
            var task = Last.ContinueWith(last => Reader.ReadLineAsync()).Unwrap();
            Last = task;
            return task;

It’s probably not the best, but it’s not too hard to at least make something that functions correctly and addresses the mentioned concerns.
Posted by David Burg on 7/27/2015 at 5:12 PM
Not changing the behavior of existing APIs is wise to avoid breaking compatibility with existing apps.

Could we get a new API for Console async input? Human input in the console is by nature a very slow I/O, how can we program an event-driven way rather than block a thread on it?
Posted by peted66616 on 1/5/2015 at 5:37 PM
To add to Ben's comments, with which I agree generally, I will point out that "synchronous" is not the same as "synchronized". I.e. implementing the Console streams so that they are _synchronized_ (which is a way of ensuring thread-safety) does not imply that operations on those streams must be _synchronous_ (which means they don't return until they complete). There are lots of examples of thread-safe asynchronous behaviors; indeed, asynchronous code would be a lot less useful if it were always inherently not thread safe.

As near as I can tell from looking at Microsoft's reference source (i.e. the implementation of the SyncTextReader class), there was an intentional design decision to make all the async operations still operate synchronously. It's almost like the person responsible for updating the TextReader class to accommodate the new async features in .NET completely misunderstood what the word "synchronized" meant.

There's really no excuse to not fix this bug. Asynchronous operations are way too useful, especially now with .NET's new support for them, to allow a junior developer's mistake to be allowed to block the use of them in .NET.
Posted by Ron [MSFT] on 11/25/2014 at 2:45 PM
The "Console I/O Streams" section of the Console Class documentation at notes that "I/O operations that use these [console input, output, and error] streams are synchronized, which means that multiple threads can read from, or write to, the streams." However, that's only a single sentence buried in the documentation. We've expanded on that sentence slightly, as well as added paragraphs to the Console.In property, the Console.ReadLine method, and the TextReader.ReadLineAsync method that note that read operations on a console stream always execute synchronously.

Thanks for bringing this issue to our attention.

--Ron Petrusha
Common Language Runtime User Education
Microsoft Corporation
Posted by Tarek [MSFT] on 11/3/2014 at 10:41 AM
Thanks for your feedback.

You are right in term of the API name indicate it is Async but the behavior is not really conforming to the Async design pattern. Because of the app compatibility, we cannot change the API name to be not Async. We'll try to clarify the issue in the documentation and we'll revisit this issue in the future releases.

Thanks again for raising this issue.
Posted by Ben Voigt on 11/2/2014 at 2:55 PM
Looking to see if this mis-feature was documented anywhere, I came across something on the StreamReader.ReadLineAsync page:

> InvalidOperationException     The reader is currently in use by a previous read operation.

So in fact the rationale is incorrect. The results of the two ReadLineAsync calls could not be interchanged, the second call would throw. That's a perfectly reasonable thing to do.

Please make console async input work as documented.
Posted by Ben Voigt on 11/2/2014 at 2:49 PM
Seriously, this "design decision" is horrible.

1. The function shouldn't carry the Async suffix if it is not asynchronous. Throw an exception, tell the user to use the blocking Read. But don't implement an interface and then break its contract.
2. The rationale for the design decision is completely bogus. The indefinite ordering of results applies to all asynchronous I/O. Call ReadLineAsync twice on a reader attached to a file, and the data could come back in either order. Ditto for BeginRead. Part of using async is not overlapping operations that compete for data, if you care which completes first. But, overlapping async console input with other async operations not involving the console is a perfectly reasonable thing to do and would have predictable results... except that this design decision has made it impossible.
3. Perhaps something in the Win32 console subsystem doesn't support overlapped console input. In that case, say it isn't provided due to limitations of the platform. But don't give a ridiculous "example" of code that is obviously broken in ways that are not related to use of the console at all. And if there isn't a platform limitation, then please fix async read on console streams.
4. If you think that simultaneous async reads from a console are a sufficiently likely mistake made by beginners to protect against, think about better ways to provide that protection. Not by neutering the async read, though. Even taking a mutex for async read so that the first one starts and only problematic parallel async reads from the same stream are blocked would be infinitely better than blocking them all. Or if the mutex is already locked an exception can be thrown, helping the user learn about correct usage of async and fixing the logic bug that leads to indeterminate pairing of input data with read operations.
Posted by ZoomZoomZoom on 3/10/2013 at 12:57 AM
"Keep reading for more details"... where are the details? I appreciate that this may be by design, but I'd appreciate a better explanation of why this was done?
Posted by Alfredo [MSFT] on 3/4/2013 at 11:56 AM

Thanks for bringing up this interesting issue. We are always grateful when customers point towards potential concerns - this helps us ensuring the quality of the .NET Framework and driving the product into the right direction.

Indeed, the behaviour you see may be unexpected, but please mind Async functions can’t always guarantee asynchronous behaviour. In this case this synchronous (blocking) behaviour was set by design in order to provide a safe implementation (keep reading for more details).

Also, thank you for providing the links with additional information. The workaround suggested in one of them works, but has one caveat. You shouldn’t use it more than once at a time.

Consider the following code:

Task<string> t1 = Task.Run(() => Console.ReadLine());
Task<string> t2 = Task.Run(() => Console.ReadLine());


string a = t1.Result;
Console.WriteLine("A = " + a);

string b = t2.Result;
Console.WriteLine("B = " + b);

In some case the values of a and b could be interchanged.

I hope this can help you work around your issue.
I thank you for your time and your contribution.

(Software Engineer on the .NET Base Class Libraries team)
Posted by Microsoft on 2/12/2013 at 8:07 PM
Thanks for your feedback.

We are rerouting this issue to the appropriate group within the Visual Studio Product Team for triage and resolution. These specialized experts will follow-up with your issue.
Posted by Microsoft on 2/12/2013 at 2:52 PM
Thank you for your feedback, we are currently reviewing the issue you have submitted. If this issue is urgent, please contact support directly(