Mises à jour périodiques des données

Je veux tout de suite faire une réservation pour que notre code s'exécute dans un environnement virtuel (machine) du .NET Framework, qui à son tour s'exécute sur un système d'exploitation à usage général, donc nous ne parlerons d'aucune précision même dans un délai de 1 à 2 ms. Néanmoins, nous essaierons de faire tout ce qui est en notre pouvoir pour augmenter la précision temporelle.

Souvent dans notre programme, il devient nécessaire de mettre à jour certaines informations avec un certain intervalle de temps. Dans mon cas, c'était une mise à jour de clichés (images) de caméras IP. Souvent, la logique métier d'une application fixe certaines limites à la fréquence des mises à jour des données. Pour ce temps, c'est 1 seconde.
La solution dans le front consiste à installer Thread.Sleep (1000) /Task.Await (1000) après la demande d'instantané.

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); } 

Mais le terme de notre opération est une quantité non déterministe. Par conséquent, la simulation de la prise d'instantanés ressemble à ceci:

Exécutez notre programme et exécutez la sortie

 [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 

Comme nous le voyons, le décalage s'accumulera et donc la logique métier de notre application sera violée.

 ,      60   1      49. 

Vous pouvez essayer de mesurer le décalage moyen et de réduire le temps de sommeil en réduisant l'écart moyen, mais dans ce cas, nous pouvons obtenir plus de demandes que notre logique métier ne l'exige. Nous ne serons jamais en mesure de prédire, sachant que la demande est traitée jusqu'à 1 seconde - combien de millisecondes nous devons attendre pour garantir la période de mise à jour nécessaire.

 ,      60   1     62. 

La solution évidente se suggère. Mesurez le temps avant et après l'opération. Et calculez leur différence.

 while (true) { int sleepMs = 1000; var watch = Stopwatch.StartNew(); watch.Start(); Getsnapshot(); watch.Stop(); int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds); Thread.Sleep(needSleepMs); } 

Exécutez notre programme maintenant. Si vous êtes chanceux, vous verrez quelque chose comme ce qui suit.

 [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 

Pourquoi ai-je écrit si j'ai de la chance? Parce que watch.Start () est exécuté avant DoSomethink () et watch.Stop () après DoSomethink (); Ces opérations ne sont pas instantanées + l'environnement d'exécution lui-même ne garantit pas la précision du temps d'exécution du programme (x). Par conséquent, il y aura des frais généraux. Notre fonction DoSomethink () fonctionne de 0 à 1 000 ms (y). Par conséquent, des situations peuvent survenir lorsque x + y> 1000 dans de tels cas

  int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds); 

prendra des valeurs négatives et nous obtiendrons une ArgumentOutOfRangeException car la méthode Thread.Sleep () ne doit pas prendre de valeurs négatives.

Dans de tels cas, il est logique de définir le temps needSleepMs à 0;
En fait, en réalité, la fonction DoSomethink () peut s'exécuter aussi longtemps que vous le souhaitez et nous pouvons obtenir le débordement variable lors du transtypage en int. Puis notre temps de sommeil
peut dépasser sleepMs;

Vous pouvez résoudre ce problème comme suit:

 var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) { needSleepMs = (int)needSleepMs; } else { needSleepMs = 0; } Thread.Sleep(needSleepMs); 

En principe, tout est prĂŞt. Mais l'utilisation de cette approche mĂŞme en un seul endroit provoque une gĂŞne pour l'Ĺ“il du programmeur. Et s'il y a des dizaines de tels endroits dans le programme, alors le code se transformera en un tas illisible ...

Pour résoudre ce problème, nous encapsulons notre code dans une fonction. Ici, vous pouvez le mettre dans une classe distincte ou utiliser Global comme un vidage normal pour la classe et l'utiliser comme statique (ma version).

Dans notre exemple, laissez-le pour plus de simplicité, laissez-le dans la classe Program

 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; } 

Notre fonction de saisie accepte un lien vers la fonction à exécuter et notre temps d'attente prévu. Et cela renvoie l'heure à laquelle notre programme doit dormir.
Pour faciliter l'utilisation, nous pouvons Ă©galement transmettre des fonctions lambda anonymes Ă  notre fonction.

Une liste complète du programme est donnée ci-dessous:

 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; } } } l' 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; } } } du 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; } } } 

Source: https://habr.com/ru/post/fr474038/


All Articles