Console.WriteLine会阻止吗?

Pet*_*son 38 .net console multithreading

Console.WriteLine在输出写入或立即返回之前是否阻塞?

如果它确实阻塞了是否有一种向控制台写入异步输出的方法?

Bri*_*eon 41

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);
  }
}
Run Code Online (Sandbox Code Playgroud)

  • @Aniket:这不会很好,因为控制台会不按顺序更新.保证写入的顺序与发出顺序相同的最佳方法是使用单个单独的线程.资源费用可以忽略不计,因为1)只创建了1个线程,2)它永远存在. (9认同)
  • @Aniket:Nah ......这是一个静态构造函数,因此每个应用程序域只运行一次. (9认同)
  • 我喜欢你的建议,因为它避免为字符串的字节创建一个中间缓冲区. (3认同)

Ste*_*lph 40

从.NET 4.5开始,TextWriter支持WriteAsyncWriteLineAsync方法,因此您现在可以使用:

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

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

  • 即使在使用 Console.Out.WriteLineAsync() 时也会阻止我;请注意,当我使用 Console.ReadKey() 等待控制台输入时它会阻塞; (2认同)
  • 如果您不在每次调用时使用“await”,那么在使用此方法时您将遇到异常,这可能会破坏您不等待写入完成而继续执行的目的。如果发生另一个 WriteAsync 时一个 WriteAsync 未完成,则会引发异常。您必须在每次调用时使用“await”同步对流的访问。 (2认同)

Fac*_*Vir 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑2 - 备注版本,基于Brian Gideon在评论中的建议(请注意,这仅涵盖可用的16个Write&WriteLine重载中的一个)

将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);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我已经切换到使用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();
    }
}
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 5

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

如果输出被重定向,它将停止缓慢.如果仍然有你的程序,那么你或许只是写一个冲击的方式太多信息.写入文件.

  • 但是,由于在控制台中单击会进入“ QuickEdit”模式,这使Console.WriteLine一直阻塞,直到您退出QuickEdit模式为止,使用异步控制台WriteLine确实非常好(几乎可以肯定是您想要的)。使用它,用户不会意外挂起您的程序或其线程之一,因为您执行了Console.WriteLine()。 (2认同)

Dre*_*kes 5

自 .NET Framework 4.5(即很久以前)以来,您已经能够通过以下方式避免阻塞当前线程:

await Console.Out.WriteLineAsync("Hello, non-blocking, world!");
Run Code Online (Sandbox Code Playgroud)