Как сохранить приложение консоли .NET?

Рассмотрим приложение консоли, которое запускает некоторые службы в отдельном потоке. Все, что нужно сделать - это подождать, пока пользователь нажмет Ctrl + C, чтобы закрыть его.

Какое из следующего является лучшим способом сделать это?

static ManualResetEvent _quitEvent = new ManualResetEvent(false);

static void Main() {
    Console.CancelKeyPress += (sender, eArgs) => {
        _quitEvent.Set();
        eArgs.Cancel = true;
    };

    // kick off asynchronous stuff 

    _quitEvent.WaitOne();

    // cleanup/shutdown and quit
}

Или это, используя Thread.Sleep(1):

static bool _quitFlag = false;

static void Main() {
    Console.CancelKeyPress += delegate {
        _quitFlag = true;
    };

    // kick off asynchronous stuff 

    while (!_quitFlag) {
        Thread.Sleep(1);
    }

    // cleanup/shutdown and quit
}

Ответ 1

вы всегда хотите предотвратить использование циклов while, особенно когда вы вынуждаете код перепроверять переменные. Он тратит ресурсы процессора и замедляет вашу программу.

Я бы определенно сказал первый.

Ответ 2

В качестве альтернативы более простым решением является просто:

Console.ReadLine();

Ответ 3

Вы можете сделать это (и удалить обработчик событий CancelKeyPress):

while(!_quitFlag)
{
    var keyInfo = Console.ReadKey();
    _quitFlag = keyInfo.Key == ConsoleKey.C
             && keyInfo.Modifiers == ConsoleModifiers.Control;
}

Не уверен, что это лучше, но мне не нравится идея вызова Thread.Sleep в цикле. Я думаю, что он чище блокировать при вводе пользователя.

Ответ 4

Я предпочитаю использовать Application.Run

static void Main(string[] args) {

   //Do your stuff here

   System.Windows.Forms.Application.Run();

   //Cleanup/Before Quit
}

из документов:

Начинает выполнение стандартного цикла сообщений приложения в текущем потоке без формы.

Ответ 5

Похоже, вы делаете это сложнее, чем вам нужно. Почему не просто Join поток после того, как вы его остановили?

class Program
{
    static void Main(string[] args)
    {
        Worker worker = new Worker();
        Thread t = new Thread(worker.DoWork);
        t.IsBackground = true;
        t.Start();

        while (true)
        {
            var keyInfo = Console.ReadKey();
            if (keyInfo.Key == ConsoleKey.C && keyInfo.Modifiers == ConsoleModifiers.Control)
            {
                worker.KeepGoing = false;
                break;
            }
        }
        t.Join();
    }
}

class Worker
{
    public bool KeepGoing { get; set; }

    public Worker()
    {
        KeepGoing = true;
    }

    public void DoWork()
    {
        while (KeepGoing)
        {
            Console.WriteLine("Ding");
            Thread.Sleep(200);
        }
    }
}

Ответ 6

Из двух первых лучше

_quitEvent.WaitOne();

потому что во втором поток, который просыпается каждую миллисекунду, превратится в прерывание ОС, которое дорого

Ответ 7

Вы должны делать это так же, как если бы вы программировали службу Windows. Вы никогда не использовали бы оператор while, вместо этого вы использовали бы делегат. WaitOne() обычно используется во время ожидания потоков для уничтожения - Thread.Sleep() - нецелесообразно. Вы думали использовать System.Timers.Timer, используя это событие, чтобы проверить событие закрытия?

Ответ 8

Также возможно заблокировать поток/программу на основе токена отмены.

token.WaitHandle.WaitOne();

WaitHandle сигнализируется, когда токен отменен.

Я видел эту технику, используемую Microsoft.Azure.WebJobs.JobHost, где токен происходит из источника токена отмены WebJobsShutdownWatcher (наблюдатель файла, который завершает задание).

Это дает некоторый контроль над тем, когда программа может закончиться.