كيف يمكنني (بشكل معقول) إجراء إجراء كل ميلي ثانية على وجه التحديد؟

StackOverflow https://stackoverflow.com/questions/2517742

سؤال

لديّ جهاز يستخدم عميل NTP للمزامنة حتى وقت الإنترنت ، لذا يجب أن تكون ساعة النظام دقيقة إلى حد ما.

لديّ تطبيق أقوم بتطوير البيانات التي تقوم بتسجيل البيانات في الوقت الفعلي ، ثم تقوم بمعالجتها ثم يمررها. ما أود القيام به الآن هو إخراج البيانات التي تتماشى كل ميلي ثانية مع ساعة النظام. على سبيل المثال ، إذا أردت أن أفعل فترات 20ms ، يجب أن تكون Oututs شيئًا من هذا القبيل:

13:15:05:000
13:15:05:020
13:15:05:040
13:15:05:060

لقد رأيت اقتراحات لاستخدام فئة ساعة التوقيت ، ولكن هذا يقيس الوقت فقط على عكس البحث عن طوابع زمنية محددة. يتم تشغيل الرمز الذي يجب القيام به في موضوعه الخاص ، لذلك يجب أن يكون مشكلة إذا كنت بحاجة إلى إجراء بعض المكالمات الحظر نسبيًا.

أي اقتراحات حول كيفية تحقيق ذلك إلى معقولة (بالقرب من أو أفضل من 1ms ستكون دقيقة) ستكون لطيفة).

هل كانت مفيدة؟

المحلول

لا أعرف مدى نجاحها مع C ++/CLR ولكن ربما تريد أن تنظر إليها توقيت الوسائط المتعددة,
Windows ليس في الوقت الفعلي حقًا ولكن هذا أقرب ما يكون

نصائح أخرى

يمكنك الحصول على ختم زمني دقيق للغاية من TimeGetTime () عند تقليل الفترة الزمنية. ستحتاج فقط إلى بعض العمل لتحويل قيمة الإرجاع إلى وقت ساعة. يعرض رمز C# هذا النهج:

using System;
using System.Runtime.InteropServices;

class Program {
    static void Main(string[] args) {
        timeBeginPeriod(1);
        uint tick0 = timeGetTime();
        var startDate = DateTime.Now;
        uint tick1 = tick0;
        for (int ix = 0; ix < 20; ++ix) {
            uint tick2 = 0;
            do {  // Burn 20 msec
                tick2 = timeGetTime();
            } while (tick2 - tick1 < 20);
            var currDate = startDate.Add(new TimeSpan((tick2 - tick0) * 10000));
            Console.WriteLine(currDate.ToString("HH:mm:ss:ffff"));
            tick1 = tick2;
        }
        timeEndPeriod(1);
        Console.ReadLine();
    }
    [DllImport("winmm.dll")]
    private static extern int timeBeginPeriod(int period);
    [DllImport("winmm.dll")]
    private static extern int timeEndPeriod(int period);
    [DllImport("winmm.dll")]
    private static extern uint timeGetTime();
}

في الفكر الثاني ، هذا مجرد قياس. للحصول على إجراء بشكل دوري ، سيتعين عليك استخدام TimeSetEvent (). طالما أنك تستخدم timebeginperiod () ، يمكنك الحصول على فترة رد الاتصال بالقرب من 1 مللي ثانية. أحدهما هو أنه سيعوض تلقائيًا عندما تأخر رد الاتصال السابق لأي سبب من الأسباب.

أفضل رهان لك هو استخدام التجميع المضمن وكتابة جزء كبير من التعليمات البرمجية كبرنامج تشغيل للجهاز.

من ذلك الطريق:

  • لديك تحكم في عدد التعليمات
  • سيكون لطلبك أولوية التنفيذ

في النهاية ، لا يمكنك ضمان ما تريده لأن نظام التشغيل يجب أن يكرم طلبات من عمليات أخرى لتشغيله ، مما يعني أن شيئًا آخر يمكن أن يكون دائمًا مشغولاً في اللحظة التي تريد أن تعمل فيها عمليتك. ولكن يمكنك تحسين الأمور باستخدام timeBeginPeriod لجعل من المرجح أن يتم تبديل العملية الخاصة بك في الوقت المناسب ، وربما تكون تكمن في كيفية انتظارك بين التكرارات - على سبيل المثال. النوم لمعظم ولكن ليس طوال الوقت ثم باستخدام حلقة مزدحمة للباقي.

حاول القيام بذلك في موضوعين. في موضوع واحد ، استخدم شيئًا مثل هذه للاستعلام عن مؤقت عالي الدقة في حلقة. عندما تكتشف طابعًا زمنيًا يتوافق مع (أو قريب من) حدود 20 مللي ثانية ، أرسل إشارة إلى مؤشر ترابط إخراج السجل الخاص بك مع الطابع الزمني للاستخدام. سوف ينتظر مؤشر ترابط إخراج السجل الخاص بك ببساطة إشارة ، ثم الاستيلاء على الطابع الزمني الذي تم تمريره وإخراج كل ما هو مطلوب. سيؤكد الحفاظ على اثنين في مؤشرات الترابط المنفصلة أن مؤشر ترابط إخراج السجل الخاص بك لا يتداخل مع المؤقت (هذا يحاكي بشكل أساسي مقاطعة مؤقت للأجهزة ، والتي ستكون هي الطريقة التي سأفعل بها على نظام أساسي مضمن).

يجب أن يكون CreateWaitAbleTimer/SetWaitableTimer وخيط عالي الأولوية دقيقة لحوالي 1 مللي ثانية. لا أعرف لماذا يحتوي حقل ميلي ثانية في مثالك على أربعة أرقام ، القيمة القصوى هي 999 (منذ 1000 مللي ثانية = 1 ثانية).

بما أنك قلت ، لا يجب أن يكون هذا مثاليًا ، فهناك شيء يمكن القيام به.

على حد علمي ، لا يوجد مؤقت يتزامن مع وقت محدد. لذلك سيتعين عليك حساب المرة القادمة وجدولة المؤقت لهذا الوقت المحدد. إذا كان الموقت الخاص بك لديه دعم دلتا فقط ، فسيتم حساب ذلك بسهولة ولكنه يضيف المزيد من الخطأ لأنه يمكن بسهولة بدء تشغيل وحدة المعالجة المركزية بين الوقت الذي تحسب فيه دلتا الخاص بك والوقت الذي يتم فيه إدخال الموقت في kernel.

كما أشير بالفعل ، فإن Windows ليس نظام التشغيل في الوقت الفعلي. لذلك يجب أن تفترض أنه حتى إذا قمت بجدولة مؤقتًا لتنطلق في ": 0010" ، فقد لا يتم تنفيذ الكود الخاص بك حتى بعد ذلك الوقت جيدًا (على سبيل المثال ، ": 0540"). طالما أنك تتعامل بشكل صحيح مع هذه المشكلات ، ستكون الأمور "موافق".

20 مللي ثانية تقريبًا طول شريحة زمنية على النوافذ. لا توجد طريقة لضرب 1 مللي ثانية من التوقيت في النوافذ بشكل موثوق دون إضافة نوع من RT مثل Intime. في Windows ، أعتقد أن خياراتك هي WaitforsingleObject و Sleepex وحلقة مزدحمة.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top