Zeit

Messung der Zeit | Vorgabe von Zeiten

An sich gibt es ja Uhren.
Aber wenn ein Prozess im Rechner bezüglich seiner Länge untersucht werden soll, braucht man eine Art innere Stopuhr. Auch für Programme, die etwas mit richtiger Zeit ("Echtzeit") zu tun haben, wie Steuerungsprogramme, geht es nicht ohne. Wie so oft im Leben, es gibt mehrere Möglichkeiten.


Zeitmessung

Bei der Zeitmessung gibt es zwei Teilaufgaben, die reine Zeitanzeige und die Messung von Zeitintervallen. Für beide benötigt man Zeitfunktionen, deren Möglichkeiten in der verlinkten Tabelle angegeben sind.
Wie man Uhren programmiert, zeigen die Beispielprogramme. Es läuft immer auf den Aufruf der Funktion "Time" auf, die die Systemzeit nach wählbarer Formatierung höchstens sekundengenau ausgibt.

Die Messung von Zeitdifferenzen beruht auf der zweimaligen Zeitnahme. Dafür stehen mehrere Funktionen zur Verfügung.

1. Lange Zeitdifferenzen
Man kann die Now- oder die Time-Funktion nutzen. Die Ausgabe ist eine Fließkommazahl, deren gebrochenen Teile die Zeit beschreiben. Die Differenz zweier Uhrzeiten ergibt, nach Formatierung, die Zeitdifferenz. Kleinste darstellbare Zeitdifferenz ist die Sekunde. Das liegt an der erforderlichen Formatierungsfunktion. Das folgende Codebeispiel zeigt Ausgaben in Sekunden bzw. in der Form "Stunden:Minuten:Sekunden"

Dim Start As Date, Dauer As Integer

Start = Time
...
Dauer = Second(Time - Start)
Label1.Caption = Format(Time - Start, "Long Time")

Achtung: Die erste Zeile ist höchst wichtig, denn sie bewirkt die Zuweisung der Datumsinformation an Double-Variable. Fehlt sie, kommt Unsinn heraus.

2. Kurze Zeitdifferenzen (Bruchteile von Sekunden)
Sollte mit einem Rechner, dessen Taktfrequenz im Gigahertz-Bereich läuft, kein Problem sein. Ist es aber dennoch.

Man kann
1.) die Timer-Funktion nutzen. Sie gibt die Zahl der Sekunden seit Mitternacht wieder, und das auch mit Bruchteilen von Sekunden. Leider wird sie aber nur in einem gewissen Rhythmus aktualisiert, der vom Betriebssystem abhängt. Um es zu verstehen, erläutere ich es mit geratenen Zahlen: Timer bestimmt die Sekundenzahl mit 4 Nachkommastellen, dies aber nur 100 mal pro Sekunde. Das bedeutet, daß bei beliebigem Aufruf nur zwei Nachkommastellen verläßlich sind. Um die Sache experimentell zu klären, nutzen wir den Timer und glauben an seine "Richtigkeit".

Dim start As Double, dauer As Double

Private Sub Form_Load()
Timer1.Enabled = False
End Sub

Private Sub Command1_Click()
Timer1.Interval = 55
Timer1.Enabled = True
start = Timer
End Sub

Private Sub Timer1_Timer()
Timer1.Enabled = False
dauer = (Timer - start)
Label1.Caption = Format(dauer, "#.### s")
End Sub

Mein Label1 meldet nicht, wie vorgesehen, 0,055 s sondern 0,062 s. Oberhalb der 0,1 s, also der 100 ms, funktioniert die Anzeige halbwegs zuverlässig.

Klug, wie wir uns finden, nehmen wir
2.) den Timer (siehe den folgenden Abschnitt!), der sich ja beliebig einstellen läßt, und lassen ihn einfach die Millisekunden zählen. Wenn das Ereignis fertig ist, fragen wir nach seinem Stand. Und siehe da, er hat, wenn inzwischen etwas Ernsthaftes lief, nicht gezählt! Der Timer wird durch ablaufende Routinen unterbrochen. Er läuft auf programmtechnisch zu "hoher" Ebene. Bleibt der Rückgriff auf "tiefere" Ebenen, auf das Betriebssystem selbst. Das wird etwas kryptisch. Ich habe es nicht erfunden, merkwürdigerweise funktioniert es trotzdem.

Deshalb
3.) nimmt man Zugriff auf die WIN32API. Das heißt, wir deklarieren, rufen speziell auf ... denken nach: Warum gibt es das nicht als Bestandteil des normalen Befehlsumfanges?
So lauten die erforderlichen Funktionen:

Public Declare Function QueryPerformanceCounter Lib "kernel32" Alias "QueryPerformanceCounter" (lpPerformanceCount As LARGE_INTEGER) As Long

Public Declare Function QueryPerformanceFrequency Lib "kernel32" Alias "QueryPerformanceFrequency" (lpFrequency As LARGE_INTEGER) As Long

Woher man sie bekommt? Zunächst unter <Add_Ins> den API Viewer laden. Öffnen und unter <Datei> den WIN32API.TXT laden. Dort die beiden Funktionen markieren und in ein Modul einfügen. Der Modul muß dann noch eine "Sub Main()" erhalten und über diese muß das Programm gestartet werden. Die Einstellung dazu gibt es unter <Projekt><Eigenschaften von Projekt ...>.

Nun könnte man loslegen, wenn nicht der Datentyp LARGE_INTEGER erforderlich wäre. Der muß eigens definiert werden. Man benötigt wenigstens eine Variable dieses Typs und eine Umwandlungsroutine, die die Abfrageergebnisse handhabbar macht. Das kann alles in den Modul.

Public Type LARGE_INTEGER
low As Long
high As Long
End Type

Global frage As LARGE_INTEGER

Public Function punkt() As Double
Dim lo As Double
QueryPerformanceCounter frage
lo = frage.low
If lo < 0 Then lo = lo + 4294967297#
punkt = frage.high * 4294967296# + lo
End Function

Public Function frequenz() As Double
QueryPerformanceFrequency frage
frequenz = frage.low
End Function

Im Programmablauf selbst braucht man Double-Variable, die Werte für Beginn, Fertig und die Frequenz speichern und für eine Zeitberechnung vorhalten.

Dim beginn As Double, fertig As Double, frequenz As Double
Dim Dauer As Double
...
beginn = punkt
'tue irgendwas
fertig = punkt
Dauer = (fertig - beginn)/frequenz 'in Sekunden

Damit kann man bis auf die Größenordnung einer Millisekunde auflösen.

zum Anfang

Zeitgabe

Um Rechnerereignisse zeitgesteuert auszulösen, benötigt man das Steuerelement "Timer": Timer
Dieser Timer ist in Schritten von 1/1000 Sekunden programmierbar, und löst regelmäßig nach einem gewünschten Vielfachen dieses Grundschrittes (Eigenschaft "Interval" [mit einem l, ich kann nichts dafür!] ein Timer-Ereignis aus. Dem läßt sich dann die gewünschte Aktivität zuordnen.

Für periodische Ereignisse gilt das folgende Codebeispiel. Es ist ein "Interval" von 1000 im Eigenschaften-Fenster eingestellt. Es wechselt sekündlich die Hintergrundfarbe des Formulars. Es ist nicht verboten, mit dem Timer-Ereignis Sinnvolleres anzustellen!

Dim n As Integer

Private Sub Timer1_Timer()
n = n + 1
If n = 16 Then n = 0
Form1.BackColor = QBColor(n)
End Sub

In den schon erwähnten Uhrenprogrammen wird der Timer lediglich benötigt, um alle Sekunde die Rechnerzeit neu auszugeben. Es ginge dort auch ohne ihn, wenn man etwa die Zeit pausenlos neu ausgäbe. Allerdings kann der teure Rechner dann außer der Zeitanzeige nichts weiter tun.

Für ein einmaliges Ereignis kann der Timer mit Timer1.Enabled=True gestartet und mit Timer1.Enabled=False gestoppt werden. Im folgenden Codebeispiel wird 23 Sekunden nach denm Drücken des Buttons Command1 "etwas" getan.

Dim Sekunden As Integer

Private Sub Form_Load()
Timer1.Enabled = False
Sekunden = 23
End Sub

Private Sub Command1_Click()
Timer1.Interval = Sekunden * 1000
Timer1.Enabled = True
End Sub

Private Sub Timer1_Timer()
'tu was
Timer1.Enabled = False
End Sub

Achtung, die Sache funktioniert nur dann, wenn der Rechner in der Pause nichts tut! Sonst wird er den Timer vergessen!

zum Anfang

© R. Hirte * 2003