Stellen Sie sich eine Konsolenanwendung vor, die einige Dienste in einem separaten Thread startet. Sie müssen lediglich warten, bis der Benutzer Strg + C drückt, um ihn herunterzufahren.
Welcher der folgenden ist der bessere Weg, dies zu tun?
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
}
Oder dies mit 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
}
sie möchten immer die Verwendung von while-Schleifen verhindern, insbesondere wenn Sie den Code dazu zwingen, Variablen erneut zu prüfen. Es verschwendet CPU-Ressourcen und verlangsamt Ihr Programm.
Ich würde definitiv die erste sagen.
Alternativ ist eine einfachere Lösung nur:
Console.ReadLine();
Sie können dies tun (und den Ereignishandler CancelKeyPress
entfernen):
while(!_quitFlag)
{
var keyInfo = Console.ReadKey();
_quitFlag = keyInfo.Key == ConsoleKey.C
&& keyInfo.Modifiers == ConsoleModifiers.Control;
}
Nicht sicher, ob das besser ist, aber ich mag die Idee nicht, Thread.Sleep
in einer Schleife aufzurufen. Ich denke, es ist sauberer, die Benutzereingaben zu blockieren.
Ich bevorzuge die Application.Run
static void Main(string[] args) {
//Do your stuff here
System.Windows.Forms.Application.Run();
//Cleanup/Before Quit
}
aus den docs:
Beginnt mit der Ausführung einer Standardanwendungsnachrichtenschleife für den aktuellen Thread ohne Formular.
Scheint, als ob Sie es schwieriger machen, als Sie brauchen. Warum nicht einfach Join
den Thread, nachdem Sie signalisiert haben, dass er aufhört?
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);
}
}
}
Von den beiden ist der erste besser
_quitEvent.WaitOne();
weil im zweiten Thread der Thread jede Millisekunde aufwacht, wird er in OS Interrupt verwandelt, was teuer ist
Es ist auch möglich, den Thread/das Programm basierend auf einem Abbruchtoken zu blockieren.
token.WaitHandle.WaitOne();
WaitHandle wird signalisiert, wenn das Token abgebrochen wird.
Ich habe diese von Microsoft.Azure.WebJobs.JobHost verwendete Technik gesehen, bei der das Token aus einer Absage-Token-Quelle des WebJobsShutdownWatcher stammt (einer Dateiüberwachung, die den Job beendet).
Dies gibt eine gewisse Kontrolle darüber, wann das Programm enden kann.
Sie sollten es genauso tun, als würden Sie einen Windows-Dienst programmieren. Sie würden niemals eine while-Anweisung verwenden, sondern einen Delegaten. WaitOne () wird im Allgemeinen verwendet, während gewartet wird, dass Threads entsorgt werden. - Thread.Sleep () - ist nicht ratsam.