2010-09-08 14 views
32

Il blocco Console.WriteLine fino a quando l'output è stato scritto o viene restituito immediatamente?Il blocco Console.WriteLine?

Se si blocca esiste un metodo di scrittura dell'uscita asincrona nella console?

risposta

33

fa Console.WriteLine blocco fino a quando l'uscita è stato scritto o ha restituire immediatamente?

Sì.

Se lo fa blocco esiste un metodo di crei output asincrono al Console?

La soluzione per scrivere sulla console senza il blocco è sorprendentemente semplice se si utilizza .NET 4.0. L'idea è di accodare i valori del testo e lasciare che un singolo thread dedicato effettui le chiamate Console.WriteLine. Lo schema produttore-consumatore è ideale qui perché conserva l'ordinamento temporale implicito quando si utilizza la classe nativa Console. Il motivo per cui .NET 4.0 semplifica questo è perché ha la classe BlockingCollection che facilita la produzione di un modello produttore-consumatore. Se non si utilizza .NET 4.0, è possibile ottenere un backport scaricando il framework 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); 
    } 
} 
+3

Mi piace il tuo suggerimento in quanto evita la creazione di un buffer intermedio per i byte della stringa. –

+0

È possibile migliorare ulteriormente utilizzando un thread del threadpool anziché creare un nuovo thread. I thread sono costosi da creare e distruggere. L'uso di un thread del threadpool lo evita. http://msdn.microsoft.com/en-us/library/4yd16hza.aspx – Aniket

+7

@Aniket: Ciò non funzionerebbe molto bene perché la console sarebbe stata aggiornata fuori servizio. Il modo migliore per garantire che le scritture si verifichino nello stesso ordine in cui sono state emesse consiste nell'utilizzare un singolo thread separato. La spesa per le risorse è trascurabile poiché 1) c'è solo 1 thread creato e 2) vive per sempre. –

-1

Sì, blocca. E non ci sono scritture di console asincrone integrate nel framework che io conosca.

+2

http: // StackOverflow .com/a/25895151/2878353 –

+1

È grandioso ... guarda la data della mia risposta r. 'async' /' await' e 'Console. * Async' non esisteva nel 2010. –

+2

Non ha detto che ha fatto il fratello –

0

Un test rapido alla mia scrivania suggerisce che sì, blocca.

Per scrivere in modo asincrono sulla console è possibile inviare le proprie scritture su un altro thread che potrebbe quindi scriverle. È possibile farlo eseguendo un altro thread che ha una coda di messaggi da scrivere o concatenando le istanze Task in modo che le tue scritture siano ordinate.

Il mio precedente suggerimento di utilizzare il pool di thread è negativo poiché non garantisce l'ordine e pertanto l'output della console potrebbe essere confuso.

+0

Questo funziona, ma il lavoro di avvio nel threadpool ha una semantica di ordine scadente per la scrittura della console. Anche se lancio il workitem A e workitem B dallo stesso thread, non c'è alcuna garanzia che verrà eseguito per primo. – blucz

+1

@blucz: questo è un ottimo punto. Non ci ho pensato. –

8

Sì, Console.WriteLine bloccherà fino a quando l'output non verrà scritto, poiché chiama il metodo Write dell'istanza del flusso sottostante. È possibile scrivere in modo asincrono per il flusso Standard Out (che è detto stream sottostante) chiamando Console.OpenStandardOutput per ottenere lo stream, quindi chiamando BeginWrite e EndWrite su tale stream; si noti che si dovrà eseguire la propria codifica delle stringhe (convertendo l'oggetto System.String in un byte []) poiché i flussi accettano solo matrici di byte.

Modifica - Alcuni codici di esempio per iniziare:

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 - Una versione alternativa, basata su suggerimento di Brian Gideon nei commenti (notare che questo copre solo una delle sedici sovraccarichi di scrittura & WriteLine che sono disponibili)

Attuare Begin/End metodi come estensioni della classe TextWriter, quindi aggiungere una classe AsyncConsole chiamarli:

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

alternativa, scrivere un TextWriter asincrono, iniettare utilizzando Console.SetOut e quindi chiamare Console.WriteLine esattamente come normale.

Il TextWriter sarebbe qualcosa di simile:

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

Nota che ho passato a utilizzare le operazioni di gestione di inizio/metodi finali: questo perché il metodo BeginWrite stessa sembra bloccare se c'è già un asincrona scrivere in corso.

Una volta che avete quella classe, basta chiamare il metodo di iniezione e ogni chiamata si effettua a Console.WriteLine, indipendentemente da dove si rendono o con i quali sovraccarico, diventerà asincrono. Comme ca:

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

     AsyncStreamWriter.InjectAsConsoleOut(); 

     Console.WriteLine(text); 

     Console.ReadLine(); 
    } 
} 
+0

Il tuo suggerimento è degno di nota e applicabile, tuttavia, nella mia situazione, vorrei evitare di allocare un buffer temporaneo. –

+0

Vero, sarebbe utile se TextWriters implementasse il modello di programmazione asincrono come fanno gli Stream; il vantaggio di questo approccio è che non è necessario creare un thread (che è sorprendentemente costoso da fare). – FacticiusVir

+0

Peccato che non sia possibile creare metodi di estensione statica. Altrimenti sarebbe stato perfetto per incapsulare la tua logica. Immagina di chiamare 'Console.BeginWriteLine' e simili. Sarebbe incredibilmente utile ed elegante. –

0

Sì, bloccherà fino a quando l'output non verrà scritto sullo schermo. Non sono sicuro che ciò sia esplicitamente indicato nella documentazione, ma è possibile verificarlo scavando nella classe Console nel reflector.In particolare il metodo InitializeStdOutError(). Quando si crea lo TextWriter per il flusso di output, imposta AutoFlush a true

6

Hmya, l'uscita della console non è particolarmente veloce. Ma questo è un "problema" che non ha mai bisogno di essere risolto. Tieni gli occhi sul premio: stai scrivendo alla console per il beneficio di un essere umano. E quella persona non è vicina a essere in grado di leggere così velocemente.

Se l'uscita viene reindirizzata, smette di essere lenta. Se questo ha ancora un impatto sul tuo programma, forse stai scrivendo semplicemente modo troppe informazioni. Scrivi invece su un file.

+1

Ma grazie al fatto che facendo clic su una console si entra in modalità "QuickEdit" che rende Console.WriteLine bloccabile fino all'uscita dalla modalità QuickEdit, avere una Console di scrittura asincrona WriteLine è incredibilmente bello (e quasi certamente quello che vuoi). Con esso, un utente non appende accidentalmente il tuo programma o uno dei suoi thread perché fai un Console.WriteLine(). – aggieNick02

20

Come di .NET 4.5, supporta i TextWriterWriteAsync e WriteLineAsync metodi, così ora si può usare:

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

e

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

+0

Hai provato questo? Funziona davvero? :) Grazie – zsf222

+0

Lo sto usando ora - funziona come un fascino! –

+1

blocchi per me anche quando si utilizza Console.Out.WriteLineAsync(); Si noti che blocca quando sto aspettando l'input della console usando Console.ReadKey(); – user1275154

Problemi correlati