2013-02-28 48 views
9

在多線程程序中使用Console.ReadKey()時出現奇怪的問題。具有多線程的Console.ReadKey()的奇怪行爲

我的問題是:爲什麼會發生這種情況?這是一個錯誤,還是因爲我在濫用Console? (請注意,控制檯是應該是線程安全的,according to the documentation

這是最簡單的代碼來解釋這一點:

using System; 
using System.Threading; 
using System.Threading.Tasks; 

namespace ConsoleApplication2 
{ 
    internal class Program 
    { 
     private static void Main(string[] args) 
     { 
      Console.WriteLine("X"); // Also try with this line commented out. 
      Task.Factory.StartNew(test); 
      Console.ReadKey(); 
     } 

     private static void test() 
     { 
      Console.WriteLine("Entering the test() function."); 
      Thread.Sleep(1000); 
      Console.WriteLine("Exiting the test() function."); 
     } 
    } 
} 

你覺得這會打印出來,如果你運行它,並唐't按下一個鍵?

答案就是你所期望:

X 
Entering the test() function. 
Exiting the test() function. 

現在註釋掉Console.WriteLine("X")並重新運行(不按任何鍵)。 我期望看到此輸出:

Entering the test() function. 
Exiting the test() function. 

相反,我看到什麼。然後當我按下一個鍵,它說:

Entering the test() function. 

......就是這樣。該程序退出(當然),它沒有時間去下一個WriteLine()

我覺得這種行爲很神祕。這很容易解決,但我對它爲什麼會發生感興趣。

[編輯]

如果我添加它的工作一個Thread.Sleep(1)立即Console.ReadKey()之前預期。當然,這不應該是必要的,因爲無論如何,Console.ReadKey()應該永遠等待。

所以看起來它可能是某種競爭條件?

更多信息:Servy發現(並且我重複了)Console.WriteLine("Entering the test() function.")行被阻塞,直到按下任何鍵。

構建配置

的Visual Studio 2012,Windows 7的64位,四核,英語(英國)。

我已經嘗試過.Net4,.Net4.5,x86,AnyCPU以及調試和發佈的所有組合,並且它們都不能在我的PC上運行。但發生了一件非常奇怪的事情。它在我第一次嘗試使用.Net4的AnyCPU版本時開始工作,但之後它再次停止工作。看起來非常像隻影響某些系統的競爭條件。

+1

根據我的實驗有一些初始化,發生第一次控制檯將被寫入建立了框架,併發讀取和寫入。如果在你第一次調用'ReadKey'之前控制檯已經寫好了,你會沒事的,但是從未寫過這個特定的行爲。正如你所說,易於解決,但很好奇。 – Servy 2013-02-28 19:36:21

+0

當您明確強制Console.Out刷新時會發生什麼? – Krypes 2013-02-28 19:36:26

+0

@Krypes'Console.WriteLine'塊(基於我的實驗),而不是繼續,不做任何事情,所以沒有機會刷新它。 – Servy 2013-02-28 19:37:12

回答

15

這是一個競賽條件。這是發生了什麼時,第一個Console.WriteLine是不是有:

  1. 創建任務,但不能運行
  2. Console.ReadKey執行,發生在Console.InternalSyncObject鎖,並阻止等待輸入
  3. 任務的Console.WriteLine調用Console.Out,它調用Console.InitializeStdOutError進行首次初始化以設置控制檯流
  4. Console.InitializeStdOutError嘗試鎖定Console.InternalSyncObject,但Console.ReadKey已經擁有它,所以它會阻止
  5. Th E用戶按下按鍵和Console.ReadKey回報解除鎖定
  6. 到Console.WriteLine的調用是暢通,並完成執行
  7. 的進程退出,因爲沒有什麼主要的ReadKey電話後
  8. 其餘在任務的代碼沒有得到機會運行

它時出現異常行爲Console.WriteLine命令留在那裏是因爲調用Console.InitializeStdOutError不與Console.ReadKey並行發生的原因。

所以簡短的回答是:是的,你濫用控制檯。您可以自己初始化控制檯(通過取消引用Console.Out),也可以在啓動Task之後但在ReadKey之前等待事件,然後在第一次調用Console.WriteLine之後讓Task發出事件信號。

+0

啊哈,所以它僵持不下。在等待I/O時鎖定它們有點頑皮! :) – 2013-02-28 20:12:25

+0

是的,沒有開玩笑!但是Console在多線程環境中出了名的問題。 – 2013-02-28 20:19:00

0

這可能是發生的,因爲它是多線程的。在您的異步任務有機會回報之前,您的主線程將繼續前進並退出。當主線程退出時,所有子線程都被終止。

如果您在ReadKey之前等待,該怎麼辦?它輸出正確嗎?

+0

OP的預期結果確實是他應該看到的,他根本不需要修改代碼。當'ReadKey'阻塞主線程時,後臺線程應該能夠寫入控制檯。 – Servy 2013-02-28 19:29:02

+0

你的意思是一個thread.sleep()?我會嘗試一下... [編輯]它確實有所作爲(但它不應該),所以我會用信息更新我的OP。 – 2013-02-28 19:29:17

2

確認.NET 4.5中的內部錯誤。 據報道,例如這裏:https://connect.microsoft.com/VisualStudio/feedback/details/778650/undocumented-locking-behaviour-in-system-console

這個工作在.NET 3.5和.NET 4

更多信息:http://blogs.microsoft.co.il/blogs/dorony/archive/2012/09/12/console-readkey-net-4-5-changes-may-deadlock-your-system.aspx

您可以使用簡單的解決方法來初始化內部結構和避免阻塞。剛(從@renestein)添加此到beggining:

Console.Error.WriteLine(); 
Console.WriteLine();