أريد أن أبدي تحفظًا على الفور بأن التعليمات البرمجية الخاصة بنا تعمل في بيئة افتراضية (الجهاز) من .NET Framework ، والتي تعمل بدورها على نظام تشغيل للأغراض العامة ، لذلك لن نتحدث عن أي دقة حتى خلال 1-2 مللي ثانية. ومع ذلك ، سنحاول القيام بكل ما في وسعنا لزيادة الدقة الزمنية.
غالبًا في برنامجنا ، يصبح من الضروري تحديث بعض المعلومات بفاصل زمني معين. في حالتي ، كان تحديثًا للقطات (صور) من كاميرات IP. في كثير من الأحيان ، يحدد منطق العمل لأحد التطبيقات حدودًا معينة لتكرار تحديثات البيانات. لهذا الوقت هو 1 ثانية.
الحل في الجبهة هو تثبيت Thread.Sleep (1000) /Task.Await (1000) بعد طلب اللقطة.
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); }
لكن مدة عمليتنا هي كمية غير حتمية. لذلك ، تبدو محاكاة أخذ لقطات مثل هذا:
تشغيل برنامجنا وتشغيل الإخراج
[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
كما نرى فإن التأخر سوف يتراكم وبالتالي سيتم انتهاك منطق العمل الخاص بطلبنا.
, 60 1 49.
يمكنك محاولة قياس متوسط التباطؤ وتقليل وقت النوم عن طريق تقليل متوسط الانحراف ، ولكن في هذه الحالة ، يمكننا الحصول على طلبات أكثر مما يتطلبه منطق أعمالنا. لن نتمكن أبدًا من التنبؤ ، مع العلم أن الطلب قد اكتمل لمدة تصل إلى ثانية واحدة - كم مللي ثانية نحتاج إلى الانتظار لضمان فترة التحديث الضرورية.
, 60 1 62.
الحل الواضح يوحي نفسه. قياس الوقت قبل وبعد العملية. وحساب فرقهم.
while (true) { int sleepMs = 1000; var watch = Stopwatch.StartNew(); watch.Start(); Getsnapshot(); watch.Stop(); int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds); Thread.Sleep(needSleepMs); }
تشغيل برنامجنا الآن. إذا كنت محظوظاً ، فسترى شيئًا مما يلي.
[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
لماذا أكتب إذا كنت محظوظا؟ لأنه يتم تنفيذ watch.Start () قبل DoSomethink () و watch.Stop () بعد DoSomethink ()؛ هذه العمليات ليست فورية + بيئة التشغيل نفسها لا تضمن دقة وقت تنفيذ البرنامج (x). لذلك ، سيكون هناك النفقات العامة. تعمل الدالة DoSomethink () الخاصة بنا من 0-1000 مللي ثانية (ص). لذلك ، قد تنشأ مواقف عند x + y> 1000 في مثل هذه الحالات
int needSleepMs = (int)(sleepMs - watch.ElapsedMilliseconds);
سيأخذ قيمًا سالبة وسنحصل على ArgumentOutOfRangeException لأن أسلوب Thread.Sleep () يجب ألا يأخذ قيمًا سلبية.
في مثل هذه الحالات ، يكون من المنطقي ضبط وقت needSleepMs على 0 ؛
في الواقع ، في الواقع ، يمكن تنفيذ الدالة DoSomethink () طالما أردت ، ويمكننا الحصول على تجاوز السعة المتغيرة عند الإرسال إلى int. ثم لدينا وقت النوم
قد يتجاوز النوم.
يمكنك إصلاح هذا على النحو التالي:
var needSleepMs = sleepMs - watch.ElapsedMilliseconds; if (needSleepMs > 0 && watch.ElapsedMilliseconds <= sleepMs) { needSleepMs = (int)needSleepMs; } else { needSleepMs = 0; } Thread.Sleep(needSleepMs);
من حيث المبدأ ، كل شيء جاهز. لكن استخدام هذا النهج حتى في مكان واحد يسبب إزعاجًا لعين المبرمج. وإذا كان هناك العشرات من هذه الأماكن في البرنامج ، فسوف يتحول الرمز إلى كومة غير قابلة للقراءة ...
لإصلاح ذلك ، نقوم بتغليف الكود الخاص بنا في إحدى الوظائف. هنا يمكنك وضعه في فصل منفصل أو استخدام Global كقلب منتظم للفئة واستخدامه كقاعدة ثابتة (الإصدار الخاص بي).
في مثالنا ، دعنا نترك الأمر للبساطة ، اتركه في فئة البرنامج
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; }
تقبل وظيفة الإدخال الخاصة بنا ارتباطًا بالوظيفة المراد تنفيذها ووقت الانتظار المخطط. ويعود الوقت الذي يجب أن ينام فيه برنامجنا.
لسهولة الاستخدام ، يمكننا أيضًا نقل وظائف lambda مجهولة إلى وظيفتنا.
فيما يلي قائمة كاملة بالبرنامج:
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; } } }