微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c# – 具有多线程的Console.ReadKey()的奇怪行为

在多线程程序中使用Console.ReadKey()时,我会遇到一个奇怪的问题.

我的问题是:为什么会发生这种情况?这是一个bug,还是因为我滥用控制台?
(注意,控制台应该是线程安全的,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.");
        }
    }
}

你认为如果运行它会打印出来,而不是按键?

答案就是你所期望的:

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()应该永远等待.

所以看起来可能是某种种族条件?

更多信息:Servy发现(和我已经重复),Console.WriteLine(“输入test()函数”)的行被阻塞,直到按下任何键.

构建配置

Visual Studio 2012,Windows 7 x64,Quad Core,英语(英国).

我已经尝试过所有的.Net4,.Net4.5,x86,Anycpu的组合,并且调试和释放,并且它们都不在我的电脑上工作.但是真的很奇怪的事情发生了.当我第一次尝试使用.Net4的Anycpu版本时,它开始工作,但是它再次停止工作.似乎非常喜欢竞争条件,只影响一些系统.

解决方法

这是一个种族条件.当第一个Console.WriteLine不在那里时,会发生什么:

>任务创建,但不运行
> Console.ReadKey执行,对Console.InternalSyncObject执行锁定,并阻止等待输入
> Task的Console.WriteLine调用Console.Out,调用Console.InitializeStdOutError进行首次初始化以设置控制台流
> Console.InitializeStdOutError尝试锁定Console.InternalSyncObject,但Console.ReadKey已经拥有它,所以它阻止
>用户按下一个键和Console.ReadKey返回,释放锁定
>对Console.WriteLine的调用被解除阻塞并完成执行
>进程退出,因为ReadKey调用后Main中没有任何内容
>任务中剩下的代码没有机会运行

当Console.WriteLine留在那里时,它的行为不同,原因是调用Console.InitializeStdOutError并不是与Console.ReadKey并行发生的.

所以简短的答案是:是的,你正在滥用控制台.您可以自己初始化控制台(通过取消引用Console.Out),或者在启动任务后,等待ReadKey之前的事件,然后在第一次调用Console.WriteLine后将Task发送给事件.

原文地址:https://www.jb51.cc/csharp/91643.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐