Domanda

Sto cercando di ricostruire una vecchia applicazione metronomo originariamente scritta utilizzando MFC in C++ per essere scritta in .NET utilizzando C#.Uno dei problemi che sto riscontrando è far "ticcare" il timer in modo sufficientemente accurato.

Ad esempio, presupponendo un BPM (battiti al minuto) semplice di 120, il timer dovrebbe spuntare ogni 0,5 secondi (o 500 millisecondi).Usarlo come base per i segni di spunta, tuttavia, non è del tutto accurato poiché .NET garantisce solo che il timer non scatti prima che sia trascorso il tempo trascorso.

Attualmente, per aggirare questo problema per lo stesso esempio di 120 BPM usato sopra, sto impostando i tick su qualcosa come 100 millisecondi e riproduco il suono del clic solo ogni 5 tick del timer.Questo migliora un po' la precisione, ma sembra un po' un trucco.

Allora, qual è il modo migliore per ottenere zecche precise?So che sono disponibili più timer rispetto al timer dei moduli Windows facilmente disponibile in Visual Studio, ma non li conosco molto bene.

È stato utile?

Soluzione

Esistono tre classi timer chiamate "Timer" in .NET.Sembra che tu stia utilizzando Windows Forms, ma in realtà potresti trovare più utile la classe System.Threading.Timer, ma fai attenzione perché richiama un thread del pool, quindi non puoi interagire direttamente con il tuo modulo da la richiamata.

Un altro approccio potrebbe essere quello di richiamare i timer multimediali Win32: timeGetTime, timeSetPeriod e così via.

Una rapida ricerca su Google ha trovato questo, che potrebbe essere utile http://www.codeproject.com/KB/miscctrl/lescsmultimediatimer.aspx

'Multimedia' (timer) è la parola d'ordine da cercare in questo contesto.

Altri suggerimenti

Che cosa utilizza l'applicazione C++?Puoi sempre utilizzare la stessa cosa o racchiudere il codice del timer da C++ in una classe C++/CLI.

Ho riscontrato questo problema durante lo sviluppo di un recente progetto di registrazione dati.Il problema con i timer .NET ( windows.forms, system.threading e system.timer ) è che sono precisi solo fino a circa 10 millisecondi, a causa della pianificazione degli eventi incorporata in .NET, credo.(Sto parlando di .NET 2 qui).Questo non era accettabile per me e quindi ho dovuto utilizzare il timer multimediale (è necessario importare la dll).Ho anche scritto una classe wrapper per tutti i timer in modo da poter passare da uno all'altro, se necessario, utilizzando modifiche minime al codice.Dai un'occhiata al mio post sul blog qui:http://www.indigo79.net/archives/27

Un'altra possibilità è che esiste un bug nell'implementazione del WPF di Dispatchertimer (esiste una discrepanza tra millisecondi e zecche che causano potenziali inesattezze a seconda del tempo esatto di esecuzione del processo), come evidenziato di seguito:

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

Le classi timer possono iniziare a comportarsi in modo strano quando il codice dell'evento "tick" del timer non ha terminato l'esecuzione nel momento in cui si verifica il "tick" successivo.Un modo per combattere questo problema è disabilitare il timer all'inizio dell'evento tick, quindi riattivarlo alla fine.

Tuttavia, questo approccio non è adatto nei casi in cui il tempo di esecuzione del codice 'tick' non è un errore accettabile nella tempistica del tick, poiché il timer sarà disabilitato (non conteggiato) durante quel tempo.

Se disabilitare il timer è un'opzione, puoi anche ottenere lo stesso effetto creando un thread separato che viene eseguito, dorme per x millisecondi, esegue, dorme, ecc...

System.Windows.Forms.Timer è limitato a una precisione di 55 millisecondi...

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top