Console.ReadKey vs Console.ReadLine с таймером

Следующий код - хорошо известный пример, показывающий разницу между сборкой отладки и выпуска:

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 расширен, блокируя основной поток?

Update

При использовании .NET 3.5 этого поведения не произойдет!

Ответ 1

Метод Console.ReadKey() блокирует Console.InternalSyncObject, тогда как метод Console.ReadLine() не работает. Когда метод TimerCallBack() пытается записать в Console, Thread ждет, потому что Console.InternalSyncObject по-прежнему заблокирован. Поэтому GC.Collect() никогда не вызывается. Как только вы нажмете клавишу, замок будет освобожден и вызывается GC.Collect().

Я изменил ваш код на следующий, который не блокирует Console.InternalSyncObject, и он только подает звуковые сигналы один раз в Release и каждые 2 секунды в Debug.

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. Подробнее здесь