views:

129

answers:

6

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?

A: 

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

Matthew Whited
A: 

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
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
@blucz: That's a very good point. I didn't think about that.
Jeff Yates
+5  A: 

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
Your suggestion is noteworthy and applicable, however, in my situation, I would like to avoid allocating a temporary buffer.
Peter Johansson
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
@FacticiusVir: 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
@Brian Gideon: That's an interesting thought...
FacticiusVir
@Brian Gideon: Two new options, inspired by your suggestion!
FacticiusVir
A: 

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
+1  A: 

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
+3  A: 

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 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);
  }
)
Brian Gideon
I like your suggestion as it avoids the creation of an intermediate buffer for the bytes of the string.
Peter Johansson