Console.WriteLine
阻塞,直到写入输出或立即返回?
如果确实阻塞了是否有一种向控制台写入异步输出的方法?
答案 0 :(得分:39)
Console.WriteLine是否阻止直到 输出已写入或已完成 马上回来?
是
如果它确实阻止了有一种方法 写异步输出到 控制台?
如果您使用的是.NET 4.0,那么在不阻塞的情况下写入控制台的解决方案是非常简单的。我们的想法是排队文本值,让一个专用线程进行Console.WriteLine
调用。生产者 - 消费者模式在这里是理想的,因为它保留了使用本地Console
类时隐含的时间顺序。 .NET 4.0之所以如此简单,是因为它具有BlockingCollection类,有助于生成生产者 - 消费者模式。如果您不使用.NET 4.0,则可以通过下载Reactive Extensions框架获得backport。
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);
}
}
答案 1 :(得分:36)
从.NET 4.5开始,TextWriter
支持WriteAsync
和WriteLineAsync
方法,因此您现在可以使用:
Console.Out.WriteAsync("...");
和
Console.Out.WriteLineAsync("...");
答案 2 :(得分:10)
是的,Console.WriteLine将阻塞,直到写入输出,因为它调用底层流实例的Write方法。您可以通过调用Console.OpenStandardOutput来获取流,然后在该流上调用BeginWrite和EndWrite,以异步方式写入标准输出流(即所谓的基础流);请注意,您必须执行自己的字符串编码(将System.String对象转换为byte []),因为流只接受字节数组。
修改 - 一些示例代码可帮助您入门:
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();
}
}
编辑2 - 备注版本,基于Brian Gideon在评论中的建议(请注意,这仅涵盖可用的Write&amp; WriteLine的16个重载中的一个)
将Begin / End方法实现为TextWriter类的扩展,然后添加一个AsyncConsole类来调用它们:
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);
}
}
编辑3
或者,编写一个异步TextWriter,使用Console.SetOut注入它,然后正常调用Console.WriteLine 。
TextWriter类似于:
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;
}
}
}
请注意,我已经切换到使用Tasks来管理Begin / End方法:这是因为如果正在进行异步写入,则BeginWrite方法本身似乎会阻塞。
一旦有了这个类,只需调用注入方法和对Console.WriteLine进行的每次调用,无论你在何处进行调用,或者哪个重载都将变为异步。 Comme ca:
class Program
{
static void Main(string[] args)
{
string text = "Hello World!";
AsyncStreamWriter.InjectAsConsoleOut();
Console.WriteLine(text);
Console.ReadLine();
}
}
答案 3 :(得分:6)
Hmya,控制台输出并不是特别快。但这是一个永远不需要修复的“问题”。密切关注奖品:为了人类的利益,你正在写控制台。而那个人并不能快速阅读。
如果输出被重定向,它将停止缓慢。如果这仍然对您的程序产生影响,那么您可能只是在编写方式太多信息。改为写入文件。
答案 4 :(得分:1)
在我的办公桌上进行快速测试会表明是的,它会阻止。
要异步写入控制台,您只需将写入发送到另一个可以将其写出的线程。您可以通过让另一个线程旋转来编写要写入的消息队列,或者通过链接Task
实例来执行此操作,以便对您的写入进行排序。
我之前使用线程池的建议很糟糕,因为它不能保证排序,因此,您的控制台输出可能会混淆。
答案 5 :(得分:0)
是的,它会阻塞,直到输出写入屏幕。我不确定文档中是否明确说明了这一点,但您可以通过挖掘反射器中的Console
类来验证这一点。特别是InitializeStdOutError()
方法。为输出流创建TextWriter
时,会将AutoFlush
设置为true
答案 6 :(得分:-1)
是的,它会阻止。并且我所知道的框架中没有构建异步控制台写入。