Console.ReadKey and Console.WriteLine deadlock

So, let’s assume we have the following code

static void Main(string[] args)
{
   var interval = Observable.Interval(TimeSpan.FromSeconds(1));

   interval.Subscribe(v => Console.WriteLine("*"));

   Console.ReadKey();
}

There’s nothing too exciting here, We’re using Reactive Extensions to periodically (every second) produce a message which we subscribe to and where we output an asterix to the console when the message arrives at the subscriber. The message is produced on it’s own thread, so we have the main thread running the Main method and the second thread should be outputting an asterix every second.

However if you run this you may well find that there’s no asterixes written to the console.

So what’s the deal ?

Whilst searching around for an answer I came across the reply to the StackOverflow question Strange behaviour of Console.ReadKey() with multithreading. Although my code is very different to that shown by the person asking the question, the reason for the failure to output an asterix to the console is the same.

Basically there’s a race condition which then causes a deadlock.

To verify this I hooked up Visual Studio to the .NET source using the information contained at Configure Visual Studio 2013 for debugging .NET framework, ran the code and then took a look at the code to verify the previously mentioned StackOverlow post…

What happens is that in a situation when the Console.ReadKey is called before Console.WriteLine is…

  • Console.ReadKey takes a lock on the Console InternalSyncObject and basically loops until it gets input
  • Next Console.WriteLine runs in it’s own thread and Console.WriteLine calls the Console.Out property which in turn calls Console.InitializeStdOutError (private static method) if Console.Out hasn’t yet been created/initialized
  • Console.InitializeStdOutError then attempts to obtain a lock on InternalSyncObject which, as we know has already been taken by Console.ReadKey, causing a deadlock until the user presses a key

To get around this we just need to stop Console.ReadKey being hit before Console.Out is initialized, which means we could call Console.WriteLine, Console.Write or Console.Out prior to Console.ReadKey and everything will work correctly as the Console.Out will have been created (and thus InternalSyncObject locked and released) prior to Console.ReadKey going into it’s loop.

So with the following code everything should work fine

static void Main(string[] args)
{
   var interval = Observable.Interval(TimeSpan.FromSeconds(1));

   Console.Write(String.Empty); // write nothing, but causes Console.Out to be initialized

   interval.Subscribe(v => Console.WriteLine("*"));

   Console.ReadKey();
}