50

Does Console.WriteLine block until the output has been written or does it return immediately?

If it does block is there a method of writing asynchronous output to the Console?

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
Peter Johansson
  • 523
  • 1
  • 4
  • 6

8 Answers8

55

Does Console.WriteLine block until the output has been written or does it return immediately?

Yes.

If it does block is there a method of writing asynchronous output to the Console?

The solution to writing to the console without blocking is surprisingly trivial if you are using .NET 4.0. The idea is to queue up the text values and let a single dedicated thread do the Console.WriteLine calls. The producer-consumer pattern is ideal here because it preserves the temporal ordering that is implicit when using the native Console class. The reason why .NET 4.0 makes this easy is because it has the BlockingCollection class which facilitates the production of a producer-consumer pattern. If you are not using .NET 4.0 then you can get a backport by downloading the Reactive Extensions framework.

public static class NonBlockingConsole
{
  private static BlockingCollection<string> m_Queue = new BlockingCollection<string>();

  static NonBlockingConsole()
  {
    var thread = new Thread(
      () =>
      {
        while (true) Console.WriteLine(m_Queue.Take());
      });
    thread.IsBackground = true;
    thread.Start();
  }

  public static void WriteLine(string value)
  {
    m_Queue.Add(value);
  }
}
Nolonar
  • 5,962
  • 3
  • 36
  • 55
Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
  • 4
    I like your suggestion as it avoids the creation of an intermediate buffer for the bytes of the string. – Peter Johansson Sep 08 '10 at 18:33
  • You can further improve this by using a threadpool thread instead of creating a new thread. Threads are expensive to create and destroy. Using a threadpool thread avoids this. http://msdn.microsoft.com/en-us/library/4yd16hza.aspx – Aniket Sep 26 '12 at 15:22
  • 14
    @Aniket: That would not work very well because the console would be updated out of order. The best way to guarantee that writes occur in the same order they were issued is to use a single separate thread. Resource expense is negligible since 1) there is only 1 thread created and 2) it lives forever. – Brian Gideon Sep 27 '12 at 00:33
  • Good point, but won't a new thread be created each time? var thread = new Thread ( blah...) tells me a new one will be created each time we enter NonBlockingConsole, right? – Aniket Sep 27 '12 at 04:16
  • 12
    @Aniket: Nah...that's a static constructor so it only runs once per app domain. – Brian Gideon Sep 27 '12 at 13:35
47

Console.WriteLine runs synchronously and the Console.Out stream will be written to before the method returns. However, for most production usage, you should be using a logging framework instead of standard out. Microsoft's logging interface explicitly does not contain asynchronous method because "Logging should be so fast that it isn't worth the performance cost of asynchronous code.".

If your use case still calls for writing to standard out and the performance cost of using async/await is worth the tradeoff then as of .NET 4.5, TextWriter supports the WriteAsync and WriteLineAsync methods, so you can now use:

Console.Out.WriteAsync("...");

and

Console.Out.WriteLineAsync("...");

Stephen Rudolph
  • 1,385
  • 14
  • 20
  • 3
    blocks for me even when using Console.Out.WriteLineAsync(); Note that it blocks when I'm waiting for console input using Console.ReadKey(); – user1275154 Jul 09 '15 at 22:33
  • @user1275154 - if the method isn't working as you expect, it may be worth it's own question – Stephen Rudolph Jul 10 '15 at 19:59
  • How reliable is this, as far as temporal order goes? – WillFM Apr 13 '16 at 16:45
  • @WillFM - I don't know that there are any guarantees, although I'd imagine that the output proceeds in-order as long as you're on the same thread of execution. If you want to ensure order though, just await the WriteAsync Task for one console output message before invoking the next. – Stephen Rudolph Apr 14 '16 at 13:47
  • 1
    It creates a thread for every message being written, hardly useful thing to do. The implementation with queue looks better – Alexei Sosin Feb 18 '20 at 09:53
  • @AlexeiS - this is a good reminder to assume someone will see a SO answer and apply it without context, thank you! .NET Core didn't exist then, but it does now, so we can check an official implementation: https://github.com/dotnet/runtime/blob/110282c71b3f7e1f91ea339953f4a0eba362a62c/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs appears to use async/await, but does not spin up a dedicated thread. My understanding is that it will either stay on the same thread or reuse an existing one in a pool. I will update my answer though, because of your larger point about usefulness. – Stephen Rudolph Feb 19 '20 at 12:45
  • @WillFM - based on this implementation in .NET Core (https://github.com/dotnet/runtime/blob/110282c71b3f7e1f91ea339953f4a0eba362a62c/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs), the temporal order is preserved. – Stephen Rudolph Feb 19 '20 at 12:45
  • 2
    You will get exceptions when using this if you don't use `await` on each call, which probably defeats your purpose of continuing execution without waiting for the write to complete. If one WriteAsync is not complete when another WriteAsync occurs, then it throws an exception. You have to synchronize access to the stream with `await` on each call. – AaronLS Apr 27 '20 at 14:50
  • @AaronLS - thank you! Do you have a good reference for that behavior? I'd be happy to update the answer to incorporate that and further dissuade folks from taking this approach. – Stephen Rudolph Apr 27 '20 at 16:00
11

Yes, Console.WriteLine will block until the output is written, as it calls the Write method of the underlying stream instance. You can write asynchronously to the Standard Out stream (which is said underlying stream) by calling Console.OpenStandardOutput to get the stream, then calling BeginWrite and EndWrite on that stream; note that you'll have to do your own string encoding (converting the System.String object to a byte[]) as streams only accept byte arrays.

Edit - Some example code to get you started:

using System;
using System.IO;
using System.Text;

class Program
{
    static void Main(string[] args)
    {
        string text = "Hello World!";

        Stream stdOut = Console.OpenStandardOutput();

        byte[] textAsBytes = Encoding.UTF8.GetBytes(text);

        IAsyncResult result = stdOut.BeginWrite(textAsBytes, 0, textAsBytes.Length, callbackResult => stdOut.EndWrite(callbackResult), null);

        Console.ReadLine();
    }
}

Edit 2 - An alternate version, based on Brian Gideon's suggestion in the comments (note that this only covers one of the sixteen overloads of Write & WriteLine that are available)

Implement Begin/End methods as extensions of the TextWriter class, then add an AsyncConsole class to call them:

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
    static void Main(string[] args)
    {
        string text = "Hello World!";

        AsyncConsole.WriteLine(text);

        Console.ReadLine();
    }
}

public static class TextWriterExtensions
{
    public static IAsyncResult BeginWrite(this TextWriter writer, string value, AsyncCallback callback, object state)
    {
        return Task.Factory.StartNew(x => writer.Write(value), state).ContinueWith(new Action<Task>(callback));
    }

    public static void EndWrite(this TextWriter writer, IAsyncResult result)
    {
        var task = result as Task;

        task.Wait();
    }

    public static IAsyncResult BeginWriteLine(this TextWriter writer, string value, AsyncCallback callback, object state)
    {
        return Task.Factory.StartNew(x => writer.WriteLine(value), state).ContinueWith(new Action<Task>(callback));
    }

    public static void EndWriteLine(this TextWriter writer, IAsyncResult result)
    {
        var task = result as Task;

        task.Wait();
    }
}

public static class AsyncConsole
{
    public static IAsyncResult Write(string value)
    {
        return Console.Out.BeginWrite(value, callbackResult => Console.Out.EndWrite(callbackResult), null);
    }

    public static IAsyncResult WriteLine(string value)
    {
        return Console.Out.BeginWriteLine(value, callbackResult => Console.Out.EndWriteLine(callbackResult), null);
    }
}

Edit 3

Alternately, write an asynchronous TextWriter, inject it using Console.SetOut and then call Console.WriteLine exactly as normal.

The TextWriter would be something like:

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public class AsyncStreamWriter
    : TextWriter
{
    private Stream stream;
    private Encoding encoding;

    public AsyncStreamWriter(Stream stream, Encoding encoding)
    {
        this.stream = stream;
        this.encoding = encoding;
    }

    public override void Write(char[] value, int index, int count)
    {
        byte[] textAsBytes = this.Encoding.GetBytes(value, index, count);

        Task.Factory.FromAsync(stream.BeginWrite, stream.EndWrite, textAsBytes, 0, textAsBytes.Length, null);
    }

    public override void Write(char value)
    {
        this.Write(new[] { value });
    }

    public static void InjectAsConsoleOut()
    {
        Console.SetOut(new AsyncStreamWriter(Console.OpenStandardOutput(), Console.OutputEncoding));
    }

    public override Encoding Encoding
    {
        get
        {
            return this.encoding;
        }
    }
}

Note that I've switched to using Tasks to manage the Begin/End methods: this is because the BeginWrite method itself seems to block if there's already an async write in progress.

Once you have that class, just call the injection method and every call you make to Console.WriteLine, regardless of where you make it or with which overload, will become asynchronous. Comme ca:

class Program
{
    static void Main(string[] args)
    {
        string text = "Hello World!";

        AsyncStreamWriter.InjectAsConsoleOut();

        Console.WriteLine(text);

        Console.ReadLine();
    }
}
FacticiusVir
  • 2,037
  • 15
  • 28
  • Your suggestion is noteworthy and applicable, however, in my situation, I would like to avoid allocating a temporary buffer. – Peter Johansson Sep 08 '10 at 18:35
  • True, it would be helpful if TextWriters implemented the Asynchronous Programming Model as Streams do; the advantage of this approach is that you don't have to create a Thread (which is surprisingly expensive to do). – FacticiusVir Sep 08 '10 at 18:47
  • Too bad you cannot create static extension methods. Otherwise that would have been perfect to encapsulate your logic. Imagine calling `Console.BeginWriteLine` and the like. That would be incredibly useful and elegant. – Brian Gideon Sep 08 '10 at 20:32
  • @Brian Gideon: Two new options, inspired by your suggestion! – FacticiusVir Sep 08 '10 at 22:19
5

Hmya, console output isn't particularly fast. But this is a 'problem' that never needs fixing. Keep your eyes on the prize: you are writing to the console for the benefit of a human. And that person isn't close to be able to read that fast.

If the output is redirected, it stops being slow. If that still has an impact on your program then you are perhaps just writing way too much info. Write to a file instead.

Hans Passant
  • 922,412
  • 146
  • 1,693
  • 2,536
  • 2
    But thanks to the fact that clicking in a console enters "QuickEdit" mode which makes Console.WriteLine block until you exit QuickEdit mode, having an async Console WriteLine is incredibly nice (and almost certainly what you want). With it, a user doesn't accidentally hang your program or one of its threads because you do a Console.WriteLine(). – aggieNick02 Apr 25 '16 at 16:57
  • 1
    I personally don't like answers like this. To say it's not a problem that needs fixing is to assume you know every possible way the console will be used in every instance. What if you're making a console based video game and you need it to function quicker than the default implementation allows? – Chris Lees Oct 07 '19 at 18:57
2

Since .NET Framework 4.5 (i.e. a long time ago) you've been able to avoid blocking the current thread with:

await Console.Out.WriteLineAsync("Hello, non-blocking, world!");
Drew Noakes
  • 300,895
  • 165
  • 679
  • 742
1

A quick test at my desk would suggest that yes, it blocks.

To asynchronously write to the console you could just send your writes to another thread that could then write them out. You could do that by either having another thread spinning that has a queue of messages to write, or by chaining Task instances so that your writes are ordered.

My previous suggestion of using the thread pool is a bad one as it won't guarantee ordering and therefore, your console output could be mixed up.

Jeff Yates
  • 61,417
  • 20
  • 137
  • 189
  • This works, but launching work in the threadpool has poor ordering semantics for console writing. Even if I launch workitem A and workitem B from the same thread, there's no guarantee which will run first. – blucz Sep 08 '10 at 17:01
0

Yes it blocks. And there are no async console writes built into the framework that I know of.

Matthew Whited
  • 22,160
  • 4
  • 52
  • 69
0

Yes it will block until the output is written to the screen. I'm not sure if this is explicitly stated in the documentation but you can verify this by digging through the Console class in reflector. In particular the InitializeStdOutError() method. When creating the TextWriter for the output stream it sets AutoFlush to true

JaredPar
  • 733,204
  • 149
  • 1,241
  • 1,454