2010-09-08 28 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