Как сохранить консольное приложение .NET? - PullRequest
93 голосов
/ 06 апреля 2010

Рассмотрим консольное приложение, которое запускает некоторые службы в отдельном потоке. Все, что нужно сделать, это дождаться, пока пользователь нажмет 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
}

Ответы [ 8 ]

58 голосов
/ 06 апреля 2010

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

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

27 голосов
/ 06 апреля 2010

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

Console.ReadLine();
12 голосов
/ 06 апреля 2010

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

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

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

9 голосов
/ 10 ноября 2014

Я предпочитаю использовать приложение . Запуск

static void Main(string[] args) {

   //Do your stuff here

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

   //Cleanup/Before Quit
}

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

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

3 голосов
/ 07 апреля 2010

Похоже, вы делаете это сложнее, чем нужно.Почему бы просто не 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);
        }
    }
}
2 голосов
/ 06 апреля 2010

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

_quitEvent.WaitOne();

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

0 голосов
/ 22 января 2019

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

token.WaitHandle.WaitOne();

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

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

Это дает некоторый контроль над моментом завершения программы.

0 голосов
/ 06 апреля 2010

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

...