Pregunta

Ambos System.Timers.Timer y System.Threading.Timer se activan a intervalos que son considerablemente diferentes de los solicitados. Por ejemplo:

new System.Timers.Timer(1000d / 20);

produce un temporizador que se dispara 16 veces por segundo, no 20.

Para asegurarme de que no hay efectos secundarios de los controladores de eventos demasiado largos, escribí este pequeño programa de prueba:

int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

// Test System.Timers.Timer
foreach (int frequency in frequencies)
{
    int count = 0;

    // Initialize timer
    System.Timers.Timer timer = new System.Timers.Timer(1000d / frequency);
    timer.Elapsed += delegate { Interlocked.Increment(ref count); };

    // Count for 10 seconds
    DateTime start = DateTime.Now;
    timer.Enabled = true;
    while (DateTime.Now < start + TimeSpan.FromSeconds(10))
        Thread.Sleep(10);
    timer.Enabled = false;

    // Calculate actual frequency
    Console.WriteLine(
        "Requested frequency: {0}\nActual frequency: {1}\n",
        frequency, count / 10d);
}

La salida se ve así:

Solicitado: 5 Hz; actual: 4,8 Hz
Solicitado: 10 Hz; actual: 9,1 Hz
Solicitado: 15 Hz; actual: 12,7 Hz
Solicitado: 20 Hz; actual: 16 Hz
Solicitado: 30 Hz; actual: 21,3 Hz
Solicitado: 50 Hz; actual: 31,8 Hz
Solicitado: 75 Hz; actual: 63,9 Hz
Solicitado: 100 Hz; actual: 63,8 Hz
Solicitado: 200 Hz; actual: 63,9 Hz
Solicitado: 500 Hz; real: 63,9 Hz

La frecuencia real se desvía hasta en un 36% de la solicitada. (Y, evidentemente, no puede exceder los 64 Hz.) Dado que Microsoft recomienda este temporizador por su "mayor precisión" sobre System.Windows.Forms.Timer , esto me desconcierta.

Por cierto, estas no son desviaciones aleatorias. Son los mismos valores cada vez. Y un programa de prueba similar para la otra clase de temporizador, System.Threading.Timer , muestra exactamente los mismos resultados.

En mi programa actual, necesito recopilar mediciones a exactamente 50 muestras por segundo. Esto aún no debería requerir un sistema en tiempo real. Y es muy frustrante obtener 32 muestras por segundo en lugar de 50.

¿Alguna idea?

@Chris: Tienes razón, todos los intervalos parecen ser múltiplos enteros de algo alrededor de 1/64 de segundo. Por cierto, agregar un Thread.Sleep (...) en el controlador de eventos no hace ninguna diferencia. Esto tiene sentido dado que System.Threading.Timer utiliza el conjunto de subprocesos, por lo que cada evento se activa en un subproceso libre.

¿Fue útil?

Solución

Bueno, en realidad estoy obteniendo un número diferente de hasta 100 Hz, con algunas desviaciones grandes, pero en la mayoría de los casos más cerca del número solicitado (ejecutando XP SP3 con los SP .NET más recientes).

El System.Timer.Timer se implementa usando System.Threading.Timer, por lo que esto explica por qué ves los mismos resultados. Supongo que el temporizador se implementa utilizando algún tipo de algoritmo de programación, etc. (es una llamada interna, tal vez mirando Rotor 2.0 podría arrojar algo de luz sobre él).

Sugeriría implementar un tipo de temporizador usando otro hilo (o una combinación de ellos) que llame a Sleep y a una devolución de llamada. Aunque no estoy seguro del resultado.

De lo contrario, puede echar un vistazo a temporizadores multimedia (Invocar).

Otros consejos

Si usa winmm.dll, puede usar más tiempo de CPU, pero tendrá un mejor control.

Aquí está su ejemplo modificado para usar los temporizadores winmm.dll

const String WINMM = "winmm.dll";
const String KERNEL32 = "kernel32.dll";

delegate void MMTimerProc (UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2);

[DllImport(WINMM)]
static extern uint timeSetEvent(
      UInt32            uDelay,      
      UInt32            uResolution, 
      [MarshalAs(UnmanagedType.FunctionPtr)] MMTimerProc lpTimeProc,  
      UInt32            dwUser,      
      Int32             fuEvent      
    );

[DllImport(WINMM)]
static extern uint timeKillEvent(uint uTimerID);

// Library used for more accurate timing
[DllImport(KERNEL32)]
static extern bool QueryPerformanceCounter(out long PerformanceCount);
[DllImport(KERNEL32)]
static extern bool QueryPerformanceFrequency(out long Frequency);

static long CPUFrequency;

static int count;

static void Main(string[] args)
{            
    QueryPerformanceFrequency(out CPUFrequency);

    int[] frequencies = { 5, 10, 15, 20, 30, 50, 75, 100, 200, 500 };

    foreach (int freq in frequencies)
    {
        count = 0;

        long start = GetTimestamp();

        // start timer
        uint timerId = timeSetEvent((uint)(1000 / freq), 0, new MMTimerProc(TimerFunction), 0, 1);

        // wait 10 seconds
        while (DeltaMilliseconds(start, GetTimestamp()) < 10000)
        {
            Thread.Sleep(1);
        }

        // end timer
        timeKillEvent(timerId);

        Console.WriteLine("Requested frequency: {0}\nActual frequency: {1}\n", freq, count / 10);
    }

    Console.ReadLine();
}

static void TimerFunction(UInt32 timerid, UInt32 msg, IntPtr user, UInt32 dw1, UInt32 dw2)
{
    Interlocked.Increment(ref count);
}

static public long DeltaMilliseconds(long earlyTimestamp, long lateTimestamp)
{
    return (((lateTimestamp - earlyTimestamp) * 1000) / CPUFrequency);
}

static public long GetTimestamp()
{
    long result;
    QueryPerformanceCounter(out result);
    return result;
}

Y aquí está la salida que obtengo:

Requested frequency: 5
Actual frequency: 5

Requested frequency: 10
Actual frequency: 10

Requested frequency: 15
Actual frequency: 15

Requested frequency: 20
Actual frequency: 19

Requested frequency: 30
Actual frequency: 30

Requested frequency: 50
Actual frequency: 50

Requested frequency: 75
Actual frequency: 76

Requested frequency: 100
Actual frequency: 100

Requested frequency: 200
Actual frequency: 200

Requested frequency: 500
Actual frequency: 500

Espero que esto ayude.

Estas clases no están diseñadas para uso en tiempo real y están sujetas a la naturaleza de programación dinámica de un sistema operativo como Windows. Si necesita ejecución en tiempo real, probablemente querrá ver algún hardware integrado. No estoy 100% seguro, pero creo que .netcpu puede ser una versión en tiempo real de un tiempo de ejecución .NET más pequeño en un chip.

http://www.arm.com/markets/emerging_applications/armpp /8070.html

Por supuesto, debe evaluar cuán importante es la precisión de esos intervalos dado que el código adjunto a ellos se ejecutará en un sistema operativo que no sea en tiempo real. A menos que, por supuesto, esta sea una pregunta puramente académica (en cuyo caso, sí, ¡es interesante! P).

Parece que sus frecuencias de temporizador reales son 63.9 Hz o múltiplos enteros de los mismos.

Eso implicaría una resolución de temporizador de aproximadamente 15 ms (o múltiplos enteros de los mismos, es decir, 30 ms, 45 ms, etc.).

Este, temporizadores basados ??en múltiplos enteros de un 'tick', es de esperar (en DOS, por ejemplo, el valor del 'tick' fue de 55 ms / 18 Hz).

No sé por qué el recuento de tics es de 15.65 mec en lugar de 15 ms. Como experimento, ¿qué sucede si duerme durante varios ms en el controlador del temporizador? ¿Podríamos estar viendo 15 ms entre puntos y 0,65 ms en su controlador del temporizador en cada clic?

Windows (y, por lo tanto, .NET se ejecuta sobre él) es un sistema operativo multitarea de forma preventiva. Cualquier subproceso dado puede ser detenido en cualquier momento por otro subproceso, y si el subproceso anterior no se comporta correctamente, no podrá recuperar el control cuando lo desee o lo necesita .

En pocas palabras, es por eso que no se puede garantizar que obtenga los tiempos exactos, y por qué Windows y .NET son plataformas inadecuadas para ciertos tipos de software. Si hay vidas en peligro porque no obtiene el control EXACTAMENTE cuando lo desea, elija una plataforma diferente.

Si necesita saltar a un entorno en tiempo real, he usado RTX en el pasado cuando se necesitaba un muestreo determinístico (desde un dispositivo serial personalizado) y tuve mucha suerte con él.

http://www.pharlap.com/rtx.htm

Aquí hay una buena implementación de un temporizador usando un temporizador multimedia http: // www. softwareinteractions.com/blog/2009/12/7/using-the-multimedia-timer-from-c.html

Hay muchas razones por las que el temporizador está apagado. Ejemplo con el hardware, el mismo hilo está ocupado con otro procesamiento, y así sucesivamente ...

Si desea una hora más precisa, use la clase Cronómetro en el espacio de nombres de System.Diagnostics.

Parte del problema es que los temporizadores tienen dos retrasos para tener en cuenta. Esto puede variar dependiendo de cómo se implementa el temporizador en el sistema operativo.

  1. La hora a la que solicita esperar
  2. El tiempo entre cuando se produce el # 1 y el proceso tiene su turno en la cola de programación.

El temporizador tiene un buen control de # 1 pero casi ningún control sobre # 2. Puede indicar al sistema operativo que le gustaría volver a ejecutarse, pero el sistema operativo es libre de activarlo cuando lo desee.

Según sus comentarios, no debería utilizar un temporizador. Debería usar un bucle con un cronómetro para verificar el intervalo y un spinlock para no perder el cuanto.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top