Console.ReadKey()与multithreading的奇怪行为

在multithreading程序中使用Console.ReadKey()时,我遇到了一个奇怪的问题。

我的问题是:为什么会这样? 这是一个错误,还是因为我滥用Console ? (请注意, 根据文档 ,控制台应该是线程安全的 。)

用代码解释这个是最容易的:

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

如果你运行它并且没有按键,你认为会打印出什么?

答案正是您所期望的:

 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()

我发现这种行为非常神秘。 它很容易解决,但我很感兴趣为什么会发生。

[编辑]

如果我在Console.ReadKey()之前立即添加Thread.Sleep(1) Console.ReadKey()它确实按预期工作。 当然,这不应该是必要的,因为Console.ReadKey()应该永远等待。

所以它看起来可能是某种竞争条件?

更多信息:Servy发现(并且我已经重复), Console.WriteLine("Entering the test() function.")被阻塞,直到按下任何键。

构建配置

Visual Studio 2012,Windows 7 x64,四核,英语(英国)。

我已经尝试过.Net4,.Net4.5,x86,AnyCPU以及调试和发布的所有组合,但它们都不能在我的电脑上运行。 但是发生了一件非常奇怪的事情。 当我第一次尝试使用.Net4的AnyCPU版本时,它开始工作,但随后又停止了工作。 看起来非常像只影响某些系统的竞争条件。

这是竞争条件。 以下是当第一个Console.WriteLine不存在时发生的情况:

  1. 任务已创建,但未运行
  2. Console.ReadKey执行,锁定Console.InternalSyncObject,并阻塞等待输入
  3. Task的Console.WriteLine调用Console.Out,调用Console.InitializeStdOutError进行首次初始化以设置控制台流
  4. Console.InitializeStdOutError尝试锁定Console.InternalSyncObject,但Console.ReadKey已经拥有它,因此它阻止
  5. 用户按下一个键,Console.ReadKey返回,释放锁定
  6. 对Console.WriteLine的调用被解除阻塞并完成执行
  7. 进程退出,因为在ReadKey调用之后Main中没有任何内容
  8. 任务中的其余代码无法运行

当Console.WriteLine留在那里时它表现不同的原因是因为对Console.InitializeStdOutError的调用没有与Console.ReadKey并行发生。

所以简短的回答是:是的,你滥用控制台。 您可以自己初始化控制台(通过解除引用Console.Out),或者在启动Task之后但在ReadKey之前等待事件,然后在第一次调用Console.WriteLine之后让Task发出事件信号。

它确认了.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

您可以使用简单的解决方法来初始化内部结构并避免阻塞。 只需将其添加到beggining(来自@renestein):

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

这可能发生,因为它是multithreading的。 在异步任务有机会报告之前,您的主要线程正在继续并退出。 当主线程退出时,所有子线程都被杀死。

如果您在ReadKey之前等待,该怎么办? 它输出正确吗?