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?
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?
Yes it blocks. And there are no async console writes built into the framework that I know of.
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.
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();
}
}
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
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.
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);
}
)