الحصول على علامات دقيقة من جهاز ضبط الوقت في C#

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

  •  08-06-2019
  •  | 
  •  

سؤال

أحاول إعادة بناء تطبيق بندول الإيقاع القديم الذي تمت كتابته في الأصل باستخدام MFC في C++ ليتم كتابته في .NET باستخدام C#.إحدى المشكلات التي أواجهها هي جعل المؤقت "يضع علامة" بدقة كافية.

على سبيل المثال، بافتراض أن BPM (نبضة في الدقيقة) سهل يبلغ 120، يجب أن يحدد المؤقت كل 0.5 ثانية (أو 500 مللي ثانية).ومع ذلك، فإن استخدام هذا كأساس لعلامات التجزئة ليس دقيقًا تمامًا لأن .NET يضمن فقط عدم ظهور مؤقتك قبل مرور الوقت المنقضي.

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

إذًا، ما هي أفضل طريقة للحصول على علامات دقيقة؟أعلم أن هناك مؤقتات متاحة أكثر من مؤقت نماذج Windows المتوفر بسهولة في Visual Studio، لكنني لست على دراية بها حقًا.

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

المحلول

توجد ثلاث فئات للمؤقت تسمى "المؤقت" في .NET.يبدو أنك تستخدم Windows Forms، ولكن في الواقع قد تجد فئة System.Threading.Timer أكثر فائدة - ولكن كن حذرًا لأنها تستدعي مرة أخرى سلسلة رسائل مجمعة، لذا لا يمكنك التفاعل مباشرة مع النموذج الخاص بك من رد الاتصال.

قد يكون هناك أسلوب آخر يتمثل في استدعاء مؤقتات الوسائط المتعددة Win32 - timeGetTime، وtimeSetPeriod، وما إلى ذلك.

وجدت جوجل سريعة هذا، والذي قد يكون مفيدا http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

"الوسائط المتعددة" (المؤقت) هي الكلمة الطنانة التي يجب البحث عنها في هذا السياق.

نصائح أخرى

ما هو تطبيق C++ الذي يستخدم؟يمكنك دائمًا استخدام نفس الشيء أو لف رمز المؤقت من C++ إلى فئة C++/CLI.

لقد واجهت هذه المشكلة عند تطوير مشروع حديث لتسجيل البيانات.المشكلة مع مؤقتات .NET (windows.forms، وsystem.threading، وsystem.timer) هي أنها دقيقة فقط حتى حوالي 10 مللي ثانية وهو ما يرجع إلى جدولة الأحداث المضمنة في .NET على ما أعتقد.(أنا أتحدث عن .NET 2 هنا).لم يكن هذا مقبولاً بالنسبة لي ولذا اضطررت إلى استخدام مؤقت الوسائط المتعددة (تحتاج إلى استيراد ملف dll).لقد كتبت أيضًا فئة مجمعة لجميع أجهزة ضبط الوقت بحيث يمكنك التبديل بينها إذا لزم الأمر باستخدام الحد الأدنى من تغييرات التعليمات البرمجية.تحقق من مشاركة مدونتي هنا:http://www.indigo79.net/archives/27

احتمال آخر هو أن هناك خطأ في تنفيذ WPF لـ DispatcherTimer (هناك عدم تطابق بين المللي ثانية والقراد الذي يسبب عدم الدقة المحتملة اعتمادًا على وقت تنفيذ العملية الدقيق) ، كما هو موضح أدناه:

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/DispatcherTimer.cs,143

class DispatcherTimer
{
    public TimeSpan Interval
    {
        set
        {
            ...
            _interval = value;
            // Notice below bug: ticks1 + milliseconds [Bug1]
            _dueTimeInTicks = Environment.TickCount + (int)_interval.TotalMilliseconds;
        }
    }
}

http://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs

class Dispatcher
{
    private object UpdateWin32TimerFromDispatcherThread(object unused)
    {
        ...
        _dueTimeInTicks = timer._dueTimeInTicks;
        SetWin32Timer(_dueTimeInTicks);
    }

    private void SetWin32Timer(int dueTimeInTicks)
    {
        ...
        // Notice below bug: (ticks1 + milliseconds) - ticks2  [Bug2 - almost cancels Bug1, delta is mostly milliseconds not ticks]
        int delta = dueTimeInTicks - Environment.TickCount; 
        SafeNativeMethods.SetTimer( 
            new HandleRef(this, _window.Value.Handle),
            TIMERID_TIMERS,
            delta); // <-- [Bug3 - if delta is ticks, it should be divided by TimeSpan.TicksPerMillisecond = 10000]
    }
}

http://referencesource.microsoft.com/#WindowsBase/Shared/MS/Win32/SafeNativeMethodsCLR.cs,505

class SafeNativeMethodsPrivate
{
    ...
    [DllImport(ExternDll.User32, SetLastError = true, ExactSpelling=true, CharSet=System.Runtime.InteropServices.CharSet.Auto)]
    public static extern IntPtr SetTimer(HandleRef hWnd, int nIDEvent, int uElapse, NativeMethods.TimerProc lpTimerFunc);
}

http://msdn.microsoft.com/en-us/library/windows/desktop/ms644906%28v=vs.85%29.aspx

uElapse [in]
Type: UINT
The time-out value, in milliseconds. // <-- milliseconds were needed eventually

يمكن أن تبدأ فئات المؤقت في التصرف بشكل غريب عندما لا ينتهي تنفيذ رمز حدث "علامة التجزئة" للمؤقت بحلول وقت حدوث "علامة التجزئة" التالية.إحدى طرق مكافحة ذلك هي تعطيل المؤقت في بداية حدث التجزئة، ثم إعادة تمكينه في النهاية.

ومع ذلك، فإن هذا الأسلوب غير مناسب في الحالات التي لا يكون فيها وقت تنفيذ رمز "العلامة" خطأً مقبولاً في توقيت العلامة، حيث سيتم تعطيل المؤقت (لا يتم احتسابه) خلال ذلك الوقت.

إذا كان تعطيل المؤقت خيارًا، فيمكنك أيضًا تحقيق نفس التأثير عن طريق إنشاء مؤشر ترابط منفصل ينفذ، وينام لمدة x ميلي ثانية، وينفذ، وينام، وما إلى ذلك...

System.Windows.Forms.Timer يقتصر على دقة 55 مللي ثانية.

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