HOWTO · C++

Hinzufügen einer zeitgesteuerten Verzögerung in C++

Dieses Tutorial gibt Ihnen eine kurze Anleitung zum Hinzufügen einer zeitgesteuerten Verzögerung in Ihrem C++-Programm.

Auf dieser Seite

Ein C++-Programm kann seinen aktuellen Thread mit einer plattformspezifischen API oder mit den Thread-Wartefunktionen der Standardbibliothek pausieren. Wählen Sie je nach Portabilität und danach, ob die Wartezeit eine Dauer oder ein Zeitpunkt auf einer Uhr ist: sleep() verwendet auf POSIX-Systemen ganze Sekunden, usleep() dort Mikrosekunden, und sleep_for() sowie sleep_until() aus C++11 akzeptieren Dauern beziehungsweise Zeitpunkte. Diese Aufrufe halten den aufrufenden Thread an; sie garantieren nicht, dass er zu einem exakt festgelegten Zeitpunkt weiterläuft.

Eine C++-API für Verzögerungen auswählen

Verwenden Sie sleep_for() für eine portable relative Wartezeit mit C++11 in gewöhnlichem Anwendungscode. sleep_until() eignet sich, wenn ein Vorgang bis zu einer Frist auf einer Uhr warten soll. Die POSIX-Funktionen sleep() und usleep() sind sinnvoll, wenn der Code ohnehin an POSIX gebunden ist; sie gehören nicht zu den portablen C++-Funktionen. Windows stellt die eigene API Sleep() bereit, deren Argument in Millisekunden angegeben wird.

Eine Verzögerung blockiert den Thread, der sie aufruft. Wenn eine Benutzeroberfläche, ein Server oder ein Thread-Pool ansprechbar bleiben muss, pausieren Sie einen dafür vorgesehenen Thread oder verwenden Sie den asynchronen Timer des Frameworks, statt den Haupt- oder Ereignisschleifen-Thread zu blockieren.

sleep() für plattformspezifische Verzögerungen in ganzen Sekunden verwenden

C++ definiert keine portable Funktion sleep(). Auf POSIX-Systemen wird sie mit <unistd.h> deklariert und mit sleep(seconds) aufgerufen. Wird die Wartezeit durch ein Signal unterbrochen, gibt die Funktion die noch angeforderte Anzahl ganzer Sekunden zurück; ist das angeforderte Intervall abgelaufen, gibt sie null zurück. Unter Windows binden Sie <Windows.h> ein und rufen Sleep(milliseconds) auf. Die Einheit ist Millisekunden, daher entspricht eine Sekunde Sleep(1000). Die POSIX- und Windows-Aufrufe unterscheiden sich in Name, Header und Einheit. Den Windows-Header erklärt auch der Windows.h-Leitfaden.

Beispielsweise lautet der POSIX-Aufruf sleep(2);, nachdem <unistd.h> eingebunden wurde. Unter Windows verwenden Sie nach dem Einbinden von <Windows.h> den Aufruf Sleep(2000);. Wählen Sie den Zweig passend zur Zielplattform; eine POSIX-Deklaration macht sleep() unter Windows nicht verfügbar.

usleep() für POSIX-Verzögerungen in Mikrosekunden verwenden

usleep() wird auf POSIX-Systemen in <unistd.h> deklariert und erwartet eine Dauer in Mikrosekunden. Zum Beispiel fordert usleep(250000); eine Pause von einer Viertelsekunde an. Die Funktion ist eine POSIX-Schnittstelle und kein portables C++; manche Plattformen oder Feature-Einstellungen stellen sie möglicherweise nicht bereit. Verwenden Sie für plattformübergreifenden C++-Code daher sleep_for(). Bei abgeschlossener Wartezeit gibt die Funktion 0 zurück, bei einer Unterbrechung oder einem anderen Fehler -1. Prüfen Sie den Rückgabewert, wenn der aufrufende Code auf eine Unterbrechung reagieren muss. Die tatsächlich verstrichene Zeit kann länger als das angeforderte Intervall sein, weil das Betriebssystem Threads nach seinem eigenen Zeitplan einteilt.

sleep_for() für eine relative Verzögerung verwenden

Binden Sie <thread> und <chrono> ein, um eine relative Dauer abzuwarten. sleep_for() hält den aktuellen Thread mindestens für die angeforderte Dauer an; Scheduling und Ressourcenkonkurrenz können die Wartezeit verlängern. Dieses vollständige C++11-Programm pausiert 200 Millisekunden und gibt anschließend eine Bestätigung aus. Führen Sie das Beispiel aus. Die unveränderte Ausgabe zeigt eine englische Bestätigung; der Code- und Ausgabeblock bleibt bytegenau erhalten.

Der Code und die angezeigten Bezeichnungen oder Ausgaben in diesem Abschnitt bleiben auf Englisch.

#include <chrono>
#include <iostream>
#include <thread>

int main() {
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
    std::cout << "Wait complete\n";
}
Wait complete

Verwenden Sie einen Dauer-Typ, der die beabsichtigte Einheit deutlich macht, etwa std::chrono::seconds(2) oder std::chrono::milliseconds(250). Die Wartezeit betrifft nur den aufrufenden Thread. Verzögerungen durch den Scheduler können dazu führen, dass mehr Zeit verstreicht als die an die Funktion übergebene Dauer.

Mit sleep_until() bis zu einem Zeitpunkt warten

Auch sleep_until() stammt aus <thread>, erwartet aber einen Zeitpunkt einer Uhr statt einer Dauer. Addieren Sie eine Dauer zum Wert now() einer Uhr, um eine zukünftige Frist zu bilden. Ein Beispiel ist std::this_thread::sleep_until(std::chrono::steady_clock::now() + std::chrono::seconds(3));. Damit wird bis zu diesem Zeitpunkt auf steady_clock gewartet. Diese Uhr ist für das Messen von Intervallen vorgesehen und wird nicht wie die Wanduhrzeit angepasst. Liegt die Frist bereits in der Vergangenheit, entsteht keine zusätzliche Wartezeit. Die Funktion gibt keinen Wert zurück; durch Scheduling kann der Thread auch erst nach der Frist weiterlaufen.

Verwenden Sie für Fristen auf Grundlage verstrichener Zeit vorzugsweise steady_clock, damit eine Korrektur der Systemuhr das Ziel nicht verschiebt. Nutzen Sie system_clock nur, wenn der Zielzeitpunkt eine Wanduhrzeit ist, und berücksichtigen Sie mögliche Änderungen dieser Uhr während des Wartens. Wie sleep_for() blockiert auch diese Funktion den aufrufenden Thread.

Die passende Verzögerung auswählen und Scheduling berücksichtigen

Verwenden Sie in portablem C++11-Code sleep_for(), wenn die Anforderung „so lange warten“ lautet, und sleep_until(), wenn bis zu einer Frist gewartet werden soll. Setzen Sie sleep() oder usleep() nur ein, wenn POSIX-spezifisches Verhalten angemessen ist; in Windows-spezifischem Code verwenden Sie Sleep(). Behalten Sie die Einheiten im Blick: sleep() erwartet Sekunden, usleep() Mikrosekunden, Windows-Sleep() Millisekunden, und <chrono> stellt Dauern mit benannten Typen dar.

Keine dieser APIs ist ein Präzisionstimer. Die angeforderte Zeit ist ein Mindestziel für die Wartezeit und keine Garantie für den genauen Zeitpunkt, zu dem der Thread weiterläuft. Scheduling, Ressourcenkonkurrenz und Unterbrechungen beeinflussen, wann die Ausführung fortgesetzt wird. Bei kurzen Wartezeiten, die keinen Thread blockieren dürfen, verwenden Sie einen asynchronen Timer der Plattform oder des Frameworks. So lässt sich eine Verzögerung auswählen, die sowohl zum Portabilitätsbedarf als auch zum Zeitmodell des Programms passt.