2010-09-08 64 views

回答

33

不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); 
    } 
} 
+3

我喜欢你的建议,因为它避免了为字符串的字节创建中间缓冲区。 – 2010-09-08 18:33:50

+0

您可以通过使用线程池线程而不是创建新线程来进一步改进。线程创建和销毁的代价很高。使用线程池线程避免了这一点。 http://msdn.microsoft.com/en-us/library/4yd16hza.aspx – Aniket 2012-09-26 15:22:16

+7

@Aniket:这不会工作得很好,因为控制台会被无序更新。确保写入按照它们发出的顺序发生的最好方法是使用一个单独的线程。资源费用是微不足道的,因为1)只有1个线程创建,2)它永远存在。 – 2012-09-27 00:33:58

-1

是的,它阻止。我知道框架中没有内置的异步控制台写入。

+2

的http://计算器.com/a/25895151/2878353 – 2015-05-27 08:07:09

+1

太棒了...看看我的回答日期河'async' /'await'和'Console。* Async'东西在2010年不存在。 – 2015-05-27 13:50:42

+2

Did not say it did brother – 2015-05-27 19:06:03

0

在我办公桌上的快速测试表明,是的,它阻止。

要异步写入控制台,您可以将写入发送到另一个线程,然后写出它们。你可以通过让另一个线程旋转来实现,它有一个消息队列来写,或者通过链接Task实例来让你的写操作是有序的。

我之前的建议是使用线程池,因为它不能保证排序,因此,控制台输出可能会混淆。

+0

This works,but launching work in the threadpool has the order semantics for console writing。即使我从同一个线程启动工作项目A和工作项目B,也无法保证首先运行。 – blucz 2010-09-08 17:01:48

+1

@blucz:这是一个很好的观点。我没有想到这一点。 – 2010-09-08 17:04:38

8

是的,Console.WriteLine会阻塞,直到写入输出为止,因为它调用底层流实例的Write方法。您可以通过调用Console.OpenStandardOutput来获取流,然后在该流上调用BeginWrite和EndWrite来异步写入标准输出流(称为基础流)请注意,您必须执行自己的字符串编码(将System.String对象转换为字节[]),因为流只接受字节数组。

编辑 - 一些示例代码,您开始:

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 - 另一种版本的基础上,布赖恩·基甸的意见建议(注意,这仅覆盖了十六个写&的WriteLine可用)的重载

实施开始/结束方法作为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; 
     } 
    } 
} 

请注意,我转向使用任务管理开始/结束的方法:这是因为BeginWrite方法本身似乎要禁止,如果已经有一个异步正在写入。

一旦你有了这个类,只需调用注射法和呼叫您对Console.WriteLine,无论在哪里你把它或与超载,将成为异步的。 Comme ca:

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

     AsyncStreamWriter.InjectAsConsoleOut(); 

     Console.WriteLine(text); 

     Console.ReadLine(); 
    } 
} 
+0

然而,在我的情况下,您的建议值得注意和适用,我想避免分配临时缓冲区。 – 2010-09-08 18:35:27

+0

是的,如果TextWriters像Streams一样实现了异步编程模型,那将会很有帮助;这种方法的优点是你不必创建一个线程(这是令人惊讶的昂贵)。 – FacticiusVir 2010-09-08 18:47:59

+0

太糟糕了,您无法创建静态扩展方法。否则,这将是完美的封装你的逻辑。想象一下,调用“Console.BeginWriteLine”等。这将是非常有用和优雅。 – 2010-09-08 20:32:23

0

是的,它会阻塞,直到输出写入屏幕。我不确定这是否在文档中明确说明,但您可以通过在反射器中挖掘Console类来验证此情况。特别是InitializeStdOutError()方法。当为输出流创建TextWriter时,它将AutoFlush设置为true

6

Hmya,控制台输出不是特别快。但这是一个永远不需要修复的'问题'。关注奖品:为了人类的利益,您正在给控制台写信。那个人不能快速阅读。

如果输出重定向,它停止缓慢。如果这仍然对你的程序有影响,那么你可能只是写的方式太多的信息。改为写入文件。

+1

但是,由于在控制台中点击进入“QuickEdit”模式,这使得Console.WriteLine阻塞,直到您退出QuickEdit模式,具有异步控制台WriteLine非常好(几乎可以肯定您想要的),这一事实。有了它,用户不会因为执行Console.WriteLine()而意外挂起程序或其中一个线程。 – aggieNick02 2016-04-25 16:57:22

20

随着.NET 4.5,TextWriter支持WriteAsyncWriteLineAsync方法,所以你现在可以使用:

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

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

+0

你试过这个吗?它真的有用吗?:)谢谢 – zsf222 2014-11-28 08:56:37

+0

我现在使用它 - 像魅力一样工作!即使在使用Console.Out.WriteLineAsync()时,我也可以使用 – 2014-12-01 15:15:57

+1

块。请注意,它在使用Console.ReadKey()等待控制台输入时会阻止; – user1275154 2015-07-09 22:33:47