2013-01-17 82 views
11

我的一位同事发现我们的代码存在问题,需要花费一段时间才能查明发生了什么,但可以通过以下简单示例最好地演示:使用Task.Run()写入控制台失败

// Fails 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Task.Run(() => Console.WriteLine("Hello World")); 
     Console.ReadKey(); 
    } 
} 

// Works fine 
class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.Write(String.Empty); 
     Task.Run(() => Console.WriteLine("Hello World")); 
     Console.ReadKey(); 
    } 
} 

从这个问题可以清楚地看出,从主线程的任何位置写入控制台都会允许后台线程写入控制台,但我们正在努力理解为什么发生这种情况。任何人都可以解释从主线程向控制台写入的内容是否实现了第一个片段不会呢?

+0

你没有描述你的失败案件会发生什么。在我的盒子里,它似乎一直挂着,直到你打回来,然后打印。 –

+0

@JonSkeet是的,这是我看到的行为 – kevinawalker

回答

4

我怀疑发生了什么事。我观察到:

  • 如果在启动之前ReadKey任何用控制台的输出,它的罚款。这包括获取Console.Out但如果你把一个延迟,使得Console.WriteLine呼叫Console.ReadKey调用之前开始,它的罚款(你可以有多个WriteLine调用ReadKey等待
不使用它
  • 我怀疑第一次使用控制台的操作获取初始化的锁(以避免它被初始化两次),并且ReadKey方法保持锁直到一个密钥被读取。躺在我到目前为止运行的每一个程序中。

    虽然执行假设初始化的操作很有趣 - 读取Console.Out“修复”了该问题,但从Console.In读取的操作没有。

    我怀疑ReadKey初始化输出,因为值仍然回显到控制台...但我不想发誓。

    有趣的是,使用Console.ReadLine()而不是Console.ReadKey()首先不会导致问题。

  • +0

    'Console.ReadLine()'不会导致相同的问题,这当然很有趣。感谢您抽出宝贵的时间 – kevinawalker

    4

    实际上,第一种情况并不失败。 “Hello World”在应用程序结束前出现。这是一个经典的Race Condition。在第一种情况下,来自主线程的Console.ReadKey()击败任务,而在第二种情况下,任务获胜。不幸的是,我不能告诉你究竟为什么写空字符串会使任务获胜。

    +0

    这确实是解决方案。 –

    +0

    我不认为这是正确的。它不像调用Console.WriteLine那样,在调用Console.ReadLine之后发生* *完成* - 否则如果您等待一段时间,您会看到输出。这似乎是某种初始化问题,我想。 –

    +0

    或者更清楚一点 - 我相信这是一种竞争条件,但仅限于第一次调用*开始时,以及仅当“Console.ReadKey”是第一次调用控制台时,两者都不会在答案中清楚。 –