Ich möchte sofort reservieren, dass unser Code in einer virtuellen Umgebung (Maschine) von .NET Framework ausgeführt wird, die wiederum auf einem Allzweckbetriebssystem ausgeführt wird, sodass wir auch innerhalb von 1-2 ms nicht über Genauigkeit sprechen. Trotzdem werden wir versuchen, alles in unserer Macht Stehende zu tun, um die zeitliche Genauigkeit zu erhöhen.
In unserem Programm ist es häufig erforderlich, einige Informationen mit einem bestimmten Zeitintervall zu aktualisieren. In meinem Fall handelte es sich um ein Update von Schnappschüssen (Bildern) von IP-Kameras. Häufig setzt die Geschäftslogik einer Anwendung der Häufigkeit von Datenaktualisierungen bestimmte Grenzen. Für diese Zeit beträgt 1 Sekunde.
Die Lösung in der Stirn besteht darin, Thread.Sleep (1000) /Task.Await (1000) nach der Snapshot-Anforderung zu installieren.
static void Getsnapshot() { var rnd = new Random() var sleepMs = rnd.Next(0, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] DoSomethink {sleepMs} ms"); Thread.Sleep(sleepMs); } while (true) { Getsnapshot(); Thread.Sleep(1000); }
Der Begriff unserer Tätigkeit ist jedoch eine nicht deterministische Größe. Daher sieht die Simulation von Schnappschüssen ungefähr so aus:
Führen Sie unser Programm aus und führen Sie die Ausgabe aus
[15:10.39] DoSomethink 974 ms [15:12.39] DoSomethink 383 ms [15:13.78] DoSomethink 99 ms [15:14.88] DoSomethink 454 ms [15:16.33] DoSomethink 315 ms [15:17.65] DoSomethink 498 ms [15:19.15] DoSomethink 708 ms [15:20.86] DoSomethink 64 ms [15:21.92] DoSomethink 776 ms [15:23.70] DoSomethink 762 ms [15:25.46] DoSomethink 123 ms [15:26.59] DoSomethink 36 ms [15:27.62] DoSomethink 650 ms [15:29.28] DoSomethink 510 ms [15:30.79] DoSomethink 257 ms [15:32.04] DoSomethink 602 ms [15:33.65] DoSomethink 542 ms [15:35.19] DoSomethink 286 ms [15:36.48] DoSomethink 673 ms [15:38.16] DoSomethink 749 ms
Wie wir sehen, wird sich die Verzögerung akkumulieren und daher wird die Geschäftslogik unserer Anwendung verletzt.
, 60 1 49.
Sie können versuchen, die durchschnittliche Verzögerung zu messen und die Schlafzeit zu verkürzen, indem Sie die durchschnittliche Abweichung verringern. In diesem Fall können wir jedoch mehr Anforderungen erhalten, als unsere Geschäftslogik erfordert. Wir werden niemals vorhersagen können, dass die Anforderung bis zu 1 Sekunde abgeschlossen ist - wie viele Millisekunden müssen wir warten, um den erforderlichen Aktualisierungszeitraum sicherzustellen.
, 60 1 62.
Die offensichtliche Lösung bietet sich an. Messen Sie die Zeit vor und nach der Operation. Und berechnen Sie ihre Differenz.
while (true) { int sleepMs = 1000; var watch = Stopwatch.StartNew(); watch.Start(); Getsnapshot(); watch.Stop(); int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds); Thread.Sleep(needSleepMs); }
Führen Sie jetzt unser Programm aus. Wenn Sie Glück haben, sehen Sie Folgendes.
[16:57.25] DoSomethink 789 ms [16:58.05] Need sleep 192 ms [16:58.25] DoSomethink 436 ms [16:58.68] Need sleep 564 ms [16:59.25] DoSomethink 810 ms [17:00.06] Need sleep 190 ms [17:00.25] DoSomethink 302 ms [17:00.55] Need sleep 697 ms [17:01.25] DoSomethink 819 ms [17:02.07] Need sleep 181 ms [17:02.25] DoSomethink 872 ms [17:03.13] Need sleep 128 ms [17:03.25] DoSomethink 902 ms [17:04.16] Need sleep 98 ms [17:04.26] DoSomethink 717 ms [17:04.97] Need sleep 282 ms [17:05.26] DoSomethink 14 ms [17:05.27] Need sleep 985 ms
Warum habe ich geschrieben, wenn ich Glück habe? Weil watch.Start () vor DoSomethink () und watch.Stop () nach DoSomethink () ausgeführt wird; Diese Operationen sind nicht augenblicklich + die Laufzeitumgebung selbst garantiert nicht die Genauigkeit der Programmausführungszeit (x). Daher wird es Overhead geben. Unsere DoSomethink () -Funktion läuft von 0-1000 ms (y). Daher können in solchen Fällen Situationen auftreten, in denen x + y> 1000 ist
int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds);
nimmt negative Werte an und wir erhalten eine ArgumentOutOfRangeException, da die Thread.Sleep () -Methode keine negativen Werte annehmen sollte.
In solchen Fällen ist es sinnvoll, die Zeit für needSleepMs auf 0 zu setzen.
In der Realität kann die DoSomethink () -Funktion so lange ausgeführt werden, wie Sie möchten, und wir können den Variablenüberlauf beim Casting in int erhalten. Dann unsere Schlafenszeit
kann sleepMs überschreiten;
Sie können dies wie folgt beheben:
var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) { needSleepMs = (int)needSleepMs; } else { needSleepMs = 0; } Thread.Sleep(needSleepMs);
Im Prinzip ist alles fertig. Die Verwendung dieses Ansatzes selbst an einer Stelle verursacht jedoch Unbehagen für das Auge des Programmierers. Und wenn es Dutzende solcher Stellen im Programm gibt, wird der Code zu einem unlesbaren Haufen ...
Um dies zu beheben, kapseln wir unseren Code in eine Funktion. Hier können Sie es in eine separate Klasse einfügen oder Global als regulären Speicherauszug für die Klasse verwenden und als statisch (meine Version) verwenden.
Lassen Sie es in unserem Beispiel der Einfachheit halber in der Program-Klasse
public static int NeedWaitMs(Action before, int sleepMs) { var watch = Stopwatch.StartNew(); watch.Start(); before(); watch.Stop(); var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) return (int) needSleepMs; return 0; }
Unsere Eingabefunktion akzeptiert eine Verknüpfung mit der auszuführenden Funktion und unserer geplanten Wartezeit. Und es gibt die Zeit zurück, die unser Programm schlafen sollte.
Zur Vereinfachung der Verwendung können wir auch anonyme Lambda-Funktionen an unsere Funktion übergeben.
Eine vollständige Liste des Programms finden Sie unten:
using System; using System.Diagnostics; using System.Threading; namespace ConsoleApp2 { class Program { static void Getsnapshot() { var rnd = new Random(); var sleepMs = rnd.Next(0, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] DoSomethink {sleepMs} ms"); Thread.Sleep(sleepMs); } static void Main(string[] args) { while (true) { var sleepMs = NeedWaitMs(Getsnapshot, 1000); Console.WriteLine($"[{DateTime.Now.ToString("mm:ss.ff")}] Need sleep {sleepMs} ms {Environment.NewLine}"); Thread.Sleep(sleepMs); } } public static int NeedWaitMs(Action before, int sleepMs) { var watch = Stopwatch.StartNew(); before(); watch.Stop(); var needSleepMs = sleepMs - watch.ElapsedMilliseconds; return needSleepMs > 0 ? (int) needSleepMs : 0; } } }