13

以下代码是一个众所周知的示例,用于显示调试和发布版本之间的区别:

using System;
using System.Threading;

public static class Program
{
    public static void Main()
    {
        Timer t = new Timer(TimerCallback, null, 0, 2000);
        Console.ReadLine();
    }

    private static void TimerCallback(Object o)
    {
        Console.WriteLine("In TimerCallback: " + DateTime.Now);
        GC.Collect();
    }
}

如果您使用调试配置运行它,计时器将每两秒输出一次当前时间。GC.Collect没有任何影响,因为编译器人为地延长了变量Timer t的寿命。在发布配置中,计时器将只执行一次。将GC.Collect垃圾收集t变量,就是这样。

这一切都像它应该的那样工作。奇怪的是,当您将 Console.ReadLine 更改为 Console.ReadKey 时,两种配置都会每两秒运行一次计时器。

Console.ReadKey 和 Console.ReadLine 有什么区别?我从文档中了解到 Console.ReadKey 阻止了发出 ReadKey 方法的线程。但是 GC.Collect 仍然会触发..

为什么Timer t通过阻塞主线程来延长生命周期?

更新

使用 .NET 3.5 时,不会发生此行为!

4

1 回答 1

10

Console.ReadKey()方法锁定,Console.InternalSyncObject而该Console.ReadLine()方法不锁定。当该TimerCallBack()方法尝试写入等待时,因为Console仍处于锁定状态。因此永远不会被调用。一旦你按下一个键,锁就会被释放并被调用。ThreadConsole.InternalSyncObjectGC.Collect()GC.Collect()

我将您的代码更改为以下不锁定的代码,Console.InternalSyncObject并且它在 Release 中仅发出一次哔声,在 Debug 中每 2 秒发出一次哔声。

private static void TimerCallback(Object o)
{
    Console.Beep();
    GC.Collect();
}

Console.WriteLine() 等待的原因是因为它在第一次Console.InternalSyncObject创建时尝试获取锁。Console.Out TextWriter

将您的代码更改为以下代码可以按预期工作,因为我们Console.Out TextWriter在启动计时器之前创建了代码。

public static void Main()
{
    Console.WriteLine("Loaded");
    Timer t = new Timer(TimerCallback, null, 0, 2000);
    Console.ReadKey();
}

private static void TimerCallback(Object o)
{
    Console.WriteLine("In TimerCallback: " + DateTime.Now);
    GC.Collect();
}

这是由于 .NET 4.5 的变化。更多信息在这里

于 2013-03-28T11:37:58.850 回答