visual - Temporizador de alta resolución en C#
usar timer c# (3)
No hay nada integrado en el framework .NET del que sea consciente. Windows tiene un mecanismo para eventos de temporizador de alta resolución a través de la API de temporizador multimedia . A continuación se muestra un ejemplo rápido que impulsé, que parece hacer el trabajo. También parece ser un buen ejemplo aquí .
Notaré que esta API cambia la configuración de todo el sistema que puede degradar el rendimiento del sistema, por lo que los compradores deben tener cuidado. Para fines de prueba, recomendaría realizar un seguimiento de la frecuencia con que se dispara el temporizador para verificar que el tiempo sea similar al dispositivo que está tratando de simular. Dado que Windows no es un sistema operativo en tiempo real, la carga en su sistema puede provocar que el temporizador MM se retrase, lo que genera intervalos de 100 ms que contienen 100 eventos en sucesión rápida, en lugar de 100 eventos espaciados 1 ms. Alguna lectura adicional en temporizadores MM.
class Program
{
static void Main(string[] args)
{
TestThreadingTimer();
TestMultimediaTimer();
}
private static void TestMultimediaTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new MultimediaTimer() { Interval = 1 })
{
timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
s.Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}
private static void TestThreadingTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
{
s.Start();
Console.ReadKey();
}
}
}
public class MultimediaTimer : IDisposable
{
private bool disposed = false;
private int interval, resolution;
private UInt32 timerId;
// Hold the timer callback to prevent garbage collection.
private readonly MultimediaTimerCallback Callback;
public MultimediaTimer()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 5;
Interval = 10;
}
~MultimediaTimer()
{
Dispose(false);
}
public int Interval
{
get
{
return interval;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
interval = value;
if (Resolution > Interval)
Resolution = value;
}
}
// Note minimum resolution is 0, meaning highest possible resolution.
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
public bool IsRunning
{
get { return timerId != 0; }
}
public void Start()
{
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
// Event type = 0, one off event
// Event type = 1, periodic event
UInt32 userCtx = 0;
timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
public event EventHandler Elapsed;
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
internal static class NativeMethods
{
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(UInt32 uTimerId);
}
¿Hay un temporizador de alta resolución que genera un evento cada vez que transcurre el temporizador, al igual que la clase System.Timer
? Necesito un temporizador de alta resolución para Elapse
cada ms.
Me sigo encontrando publicaciones que explican que el cronómetro puede medir resoluciones altas, pero no quiero medir el tiempo, quiero crear un intervalo de 1 ms.
¿Hay algo en .NET o voy a escribir mi propio temporizador de alta resolución?
Intenta crear nuevos System.Threading.Thread
y usando System.Threading.Thread.Sleep
.
var thrd = new Syatem.Threading.Thread(() => {
while (true) {
// do something
System.Threading.Thread.Sleep(1); // wait 1 ms
}
});
thrd.Start();
Puede tener un temporizador de alta resolución basado en Cronómetro que podría proporcionar incluso una resolución mucho mejor que 1ms en los sistemas actuales.
Aquí está mi otra respuesta en https://.com/a/45097518/548894
Una implementación https://gist.github.com/DraTeots/436019368d32007284f8a12f1ba0f545
Funciona en todas las plataformas y es de alta precisión donde
StopWatch.IsHighPrecision == true
Se garantiza que su evento
Elapsed
no se superpondrá (lo que podría ser importante saber, ya que los cambios de estado dentro del controlador de eventos pueden quedar desprotegidos frente al acceso de múltiples hilos)
Aquí es cómo usarlo:
Console.WriteLine($"IsHighResolution = {HighResolutionTimer.IsHighResolution}");
Console.WriteLine($"Tick time length = {HighResolutionTimer.TickLength} [ms]");
var timer = new HighResolutionTimer(0.5f);
// UseHighPriorityThread = true, sets the execution thread
// to ThreadPriority.Highest. It doesn''t provide any precision gain
// in most of the cases and may do things worse for other threads.
// It is suggested to do some studies before leaving it true
timer.UseHighPriorityThread = false;
timer.Elapsed += (s, e) => { /*... e.Delay*/ }; // The call back with real delay info
timer.Start();
timer.Stop(); // by default Stop waits for thread.Join()
// which, if called not from Elapsed subscribers,
// would mean that all Elapsed subscribers
// are finished when the Stop function exits
timer.Stop(joinThread:false) // Use if you don''t care and don''t want to wait
Aquí hay un punto de referencia (y un ejemplo en vivo):
https://gist.github.com/DraTeots/5f454968ae84122b526651ad2d6ef2a3
Los resultados de configurar el temporizador durante 0.5 ms en Windows 10:
También vale la pena mencionar que:
Tenía la misma precisión en mono en Ubuntu.
Mientras jugaba con el punto de referencia, el máximo y una desviación muy rara que vi fue de aproximadamente 0.5 ms (lo que probablemente no significa nada, no son sistemas en tiempo real, pero aún así vale la pena mencionarlos)
Las marcas de cronómetro no son marcas de TimeSpan. En esa máquina Windows 10 HighResolutionTimer.TickLength es 0.23 [ns].