Ответ 1
вы всегда хотите предотвратить использование циклов while, особенно когда вы вынуждаете код перепроверять переменные. Он тратит ресурсы процессора и замедляет вашу программу.
Я бы определенно сказал первый.
Рассмотрим приложение консоли, которое запускает некоторые службы в отдельном потоке. Все, что нужно сделать - это подождать, пока пользователь нажмет 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
}
вы всегда хотите предотвратить использование циклов while, особенно когда вы вынуждаете код перепроверять переменные. Он тратит ресурсы процессора и замедляет вашу программу.
Я бы определенно сказал первый.
В качестве альтернативы более простым решением является просто:
Console.ReadLine();
Вы можете сделать это (и удалить обработчик событий CancelKeyPress
):
while(!_quitFlag)
{
var keyInfo = Console.ReadKey();
_quitFlag = keyInfo.Key == ConsoleKey.C
&& keyInfo.Modifiers == ConsoleModifiers.Control;
}
Не уверен, что это лучше, но мне не нравится идея вызова Thread.Sleep
в цикле. Я думаю, что он чище блокировать при вводе пользователя.
Я предпочитаю использовать Application.Run
static void Main(string[] args) {
//Do your stuff here
System.Windows.Forms.Application.Run();
//Cleanup/Before Quit
}
из документов:
Начинает выполнение стандартного цикла сообщений приложения в текущем потоке без формы.
Похоже, вы делаете это сложнее, чем вам нужно. Почему не просто 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);
}
}
}
Из двух первых лучше
_quitEvent.WaitOne();
потому что во втором поток, который просыпается каждую миллисекунду, превратится в прерывание ОС, которое дорого
Вы должны делать это так же, как если бы вы программировали службу Windows. Вы никогда не использовали бы оператор while, вместо этого вы использовали бы делегат. WaitOne() обычно используется во время ожидания потоков для уничтожения - Thread.Sleep() - нецелесообразно. Вы думали использовать System.Timers.Timer, используя это событие, чтобы проверить событие закрытия?
Также возможно заблокировать поток/программу на основе токена отмены.
token.WaitHandle.WaitOne();
WaitHandle сигнализируется, когда токен отменен.
Я видел эту технику, используемую Microsoft.Azure.WebJobs.JobHost, где токен происходит из источника токена отмены WebJobsShutdownWatcher (наблюдатель файла, который завершает задание).
Это дает некоторый контроль над тем, когда программа может закончиться.