Подтвердить что ты не робот

Блокирует Console.WriteLine?

Блокирует ли Console.WriteLine до тех пор, пока результат не будет записан или он не вернется немедленно?

Если это блок, существует ли способ записи асинхронного вывода в консоль?

4b9b3361

Ответ 1

Делает ли Console.WriteLine до тех пор, пока вывод был написан или он немедленно вернуться?

Да.

Если блок блокирует метод запись асинхронного вывода в Консоль?

Решение для записи на консоль без блокировки удивительно тривиально, если вы используете .NET 4.0. Идея состоит в том, чтобы помещать в очередь текстовые значения и позволять одному выделенному потоку выполнять вызовы Console.WriteLine. Модель производителя-потребителя идеальна здесь, потому что она сохраняет временное упорядочение, которое является неявным при использовании родного класса Console. Причина, по которой .NET 4.0 делает это проще, состоит в том, что она имеет класс BlockingCollection, который облегчает производство шаблона производителя-потребителя. Если вы не используете .NET 4.0, вы можете получить обратную ссылку, загрузив Reactive Extensions.

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);
  }
}

Ответ 2

Начиная с .NET 4.5, TextWriter поддерживает методы WriteAsync и WriteLineAsync, поэтому теперь вы можете использовать:

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

и

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

Ответ 3

Да, 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. Альтернативная версия на основе предложения Брайана Гидеона в комментариях (обратите внимание, что это касается только одной из шестнадцати перегрузок 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);
    }
}

Изменить 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;
        }
    }
}

Обратите внимание, что я переключился на использование Заданий для управления методами 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();
    }
}

Ответ 4

Hmya, выход консоли не особенно быстрый. Но это "проблема", которая никогда не нуждается в исправлении. Следите за призом: вы пишете на консоль в интересах человека. И этот человек не близок к тому, чтобы читать это быстро.

Если выход перенаправлен, он перестает быть медленным. Если это все еще влияет на вашу программу, вы, возможно, просто пишете слишком много информации. Вместо этого напишите файл.

Ответ 5

Да, он блокирует. И нет встроенных в консоль записей async-консоли, которые я знаю.

Ответ 6

Быстрый тест на моем столе подскажет, что да, он блокирует.

Чтобы асинхронно писать на консоль, вы можете просто отправить свои записи в другой поток, который затем может их записать. Это можно сделать либо с помощью другого потока, который имеет очередь сообщений для записи, либо путем привязки экземпляров Task, чтобы упорядочить ваши записи.

Мое предыдущее предложение об использовании пула потоков является плохим, так как оно не гарантирует упорядочение, и поэтому вывод консоли может быть запутан.

Ответ 7

Да, он будет блокироваться до тех пор, пока на экран не будет выведен выход. Я не уверен, что это явно указано в документации, но вы можете проверить это, выкапывая класс Console в отражателе. В частности, метод InitializeStdOutError(). При создании TextWriter для выходного потока он устанавливает AutoFlush в true