Frage

Beide System.Timers.Timer und System.Threading.Timer Feuer in Intervallen, die sich erheblich von den angeforderten unterscheiden. Zum Beispiel:

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

ergibt einen Timer, der 16 Mal pro Sekunde feuert, nicht 20.

Um sicher zu sein, dass es keine Nebenwirkungen von zu langen Event-Handlern gibt, habe ich dieses kleine Testprogramm geschrieben:

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);
}

Die Ausgabe sieht so aus:

Angefordert: 5 Hz; tatsächlich: 4,8 Hz
Angefordert: 10 Hz; tatsächlich: 9,1 Hz
Angefordert: 15 Hz; tatsächlich: 12,7 Hz
Angefordert: 20 Hz; tatsächlich: 16 Hz
Angefordert: 30 Hz; tatsächlich: 21,3 Hz
Angefordert: 50 Hz; Tatsächlich: 31,8 Hz
Angefordert: 75 Hz; tatsächlich: 63,9 Hz
Angefordert: 100 Hz; tatsächlich: 63,8 Hz
Angefordert: 200 Hz; tatsächlich: 63,9 Hz
Angefordert: 500 Hz; tatsächlich: 63,9 Hz

Die tatsächliche Frequenz weicht von bis zu 36% von der angeforderten ab. (Und offensichtlich darf 64 Hz nicht überschreiten.) Angesichts der Tatsache, dass Microsoft diesen Timer für seine "größere Genauigkeit" empfiehlt System.Windows.Forms.Timer, Das verwirrt mich.

Übrigens sind dies keine zufälligen Abweichungen. Sie sind jedes Mal die gleichen Werte. Und ein ähnliches Testprogramm für die andere Timerklasse, System.Threading.Timer, zeigt genau die gleichen Ergebnisse.

In meinem tatsächlichen Programm muss ich Messungen mit genau 50 Proben pro Sekunde sammeln. Dies sollte noch kein Echtzeitsystem erfordern. Und es ist sehr frustrierend, 32 Proben pro Sekunde statt 50 zu erhalten.

Irgendwelche Ideen?

@Chris: Sie haben Recht, die Intervalle scheinen alle mit 1/64 Sekunden ein ganzzahliger Vielfaches von etwas zu sein. Übrigens, fügen Sie einen Thread hinzu. Das macht das sinnvoll, wenn man das ist System.Threading.Timer Verwendet den Thread -Pool, sodass jedes Ereignis auf einen kostenlosen Thread abgefeuert wird.

War es hilfreich?

Lösung

Nun, ich bekomme mit einigen großen Abweichungen eine unterschiedliche Zahl bis zu 100 Hz, aber in den meisten Fällen näher an der angeforderten Zahl (Ausführen von XP SP3 mit den letzten .NET -SPS).

Das System.timer.timer wird mit System.threading.timer implementiert. Dies erklärt, warum Sie die gleichen Ergebnisse sehen. Ich nehme an, dass der Timer mit einer Art Planungsalgorithmus usw. implementiert wird (es ist der interne Anruf, vielleicht schauen Sie sich Rotor 2.0 möglicherweise Licht darauf).

Ich würde vorschlagen, eine Art Timer mit einem anderen Thread (oder einer Kombination davon) zu implementieren, die den Schlaf und einen Rückruf nennt. Ich bin mir jedoch nicht sicher über das Ergebnis.

Andernfalls könnten Sie einen Blick auf die Multimedia -Timer (Pinvoke).

Andere Tipps

Wenn Sie WinMm.dll verwenden, können Sie mehr CPU -Zeit verwenden, haben jedoch eine bessere Kontrolle.

Hier ist Ihr Beispiel, das für die Verwendung der WinM.dll -Timer geändert wurde

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;
}

Und hier ist die Ausgabe, die ich bekomme:

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

Hoffe das hilft.

Diese Klassen sind nicht für die Echtzeitverwendung gedacht und unterliegen der dynamischen Zeitplanung eines Betriebssystems wie Windows. Wenn Sie eine Echtzeit-Ausführung benötigen, möchten Sie sich wahrscheinlich eine eingebettete Hardware ansehen. Ich bin mir nicht 100% sicher, aber ich denke, .NETCPU kann eine Echtzeitversion einer kleineren .NET-Laufzeit auf einem Chip sein.

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

Natürlich - Sie müssen bewerten, wie wichtig die Genauigkeit dieser Intervalle angegeben ist, dass der mit ihnen verbundene Code auf einem Nicht -Real -Time -Betriebssystem ausgeführt wird. Es sei denn natürlich, dies ist eine rein akademische Frage (in diesem Fall - ja, es ist interessant!: P).

Es sieht so aus, als ob Ihre tatsächlichen Timerfrequenzen 63,9 Hz oder Integer Multiples davon sind.

Dies würde eine Timerauflösung von etwa 15 ms (oder ganzzahliger Multiplikatoren, dh 30 ms, 45 ms usw.) implizieren.

Dies ist, Timer, die auf Ganzzahl -Vielfachen eines "Ticks" basieren, zu erwarten (in DOS beispielsweise betrug der Wert "Tick" 55 ms / 18 Hz).

Ich weiß nicht, warum Ihre Zeckenzahl 15,65 MEC anstelle von sogar 15 ms beträgt. Was ist, wenn Sie für mehrere MSC im Timer -Handler schlafen: Könnten wir bei jeder Zecke 15 ms zwischen Zecken und 0,65 ms in Ihrem Timer -Handler sehen?

Windows (und daher .NET, das darüber läuft) ist ein präventiv Multitasking -Betriebssystem. Jeder bestimmte Thread kann jederzeit durch einen anderen Thread gestoppt werden, und wenn sich der Thread vor dem Vorgang nicht richtig verhalten, werden Sie die Kontrolle nicht zurückerhalten, wenn Sie es möchten oder brauchen es.

Kurz gesagt, Sie können nicht garantiert, dass Sie genaue Zeiten erhalten und warum Windows und .NET für bestimmte Software -Arten ungeeignete Plattformen sind. Wenn das Leben in Gefahr ist, weil Sie nicht genau die Kontrolle haben, wenn Sie es möchten, wählen Sie eine andere Plattform.

Wenn Sie den Sprung in eine Echtzeitumgebung machen müssen, habe ich in der Vergangenheit RTX verwendet, als eine deterministische Probenahme benötigt wurde (von einem benutzerdefinierten seriellen Gerät) und sehr viel Glück damit hatte.

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

Hier ist eine nette Implementierung eines Timers mit Multimedia -Timerhttp://www.softwareInteractions.com/blog/2009/12/7/using-the-multimedia-timer-from-c.html

Es gibt viele Gründe dafür, dass die Timer frequenzen. Beispiel mit der Hardware, der gleiche Thread ist mit einer anderen Verarbeitung beschäftigt und so weiter ...

Wenn Sie genauer Zeit wünschen, verwenden Sie die Stoppuhrklasse im System. Diagnostics Namespace.

Ein Teil des Problems ist, dass Timer zwei haben Verzögerungen zu berücksichtigen. Dies kann abhängig von der Implementierung des Timers im Betriebssystem variieren.

  1. Die Zeit, auf die Sie warten, um darauf zu warten
  2. Die Zeit zwischen dem Zeitpunkt, an dem #1 auftritt und der Prozess in der Planungswarteschlange dreht.

Der Timer hat eine gute Kontrolle über #1, aber fast keine Kontrolle über #2. Es kann dem Betriebssystem signalisieren, dass es gerne wieder laufen würde, aber das Betriebssystem kann es frei wecken, wenn es sich so anfühlt.

Basierend auf Ihren Kommentaren sollten Sie überhaupt keinen Timer verwenden. Sie sollten eine Schleife mit einer Stoppuhr verwenden, um das Intervall und ein Spinlock zu überprüfen, damit Sie das Quantum nicht verlieren.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top