Apps, die nach dem Fortsetzen aus dem Schlaf kaputtgehen — Wie Windows-Energieereignisse funktionieren und wie Sie Geschäftsanwendungen bauen, die sie überstehen

· · Windows, Energieverwaltung, Windows-Entwicklung, Geschäftsanwendungen, Gerätesteuerung, Fehleruntersuchung, Win32-API

„Ich habe den Laptop zugeklappt, am nächsten Morgen aufgemacht, und die Geschäftsanwendung war voller Fehler.“ „Die Geräteüberwachungs-App verliert Daten nur nach dem Mittagessen.“ „Das residente Werkzeug, das nach Excel exportiert, bleibt manchmal mit einem Verbindungsfehler stehen.“ — Diese Tickets teilen einen einzigen Verdächtigen. Schlaf.

Geschäftsanwendungen aus der Zeit, in der Desktop-PCs der Mainstream waren, wurden unter der unausgesprochenen Annahme geschrieben, dass „der PC anbleibt“. Der Haupteinsatzort heute ist der Laptop, und standardmäßig schläft er nach wenigen Minuten Leerlauf. Auf einer Modern-Standby-fähigen Maschine hat sich die Semantik des Schlafs selbst gegenüber dem traditionellen Modell geändert. An Entwickler gerichtet, die Geschäftsanwendungen und Gerätesteuerungssoftware unter Windows schreiben, ordnet dieser Artikel anhand von Primärquellen, was das Betriebssystem einer App vor und nach dem Schlaf mitteilt, was kaputtgeht und wie Sie eine App schreiben, die das Fortsetzen übersteht.

1. Zuerst das Fazit

  • Schlaf ist ein Ereignis, das die App „kein Recht hat abzulehnen“. Sie werden kurz vorher mit WM_POWERBROADCAST (PBT_APMSUSPEND) benachrichtigt, aber die Schonfrist beträgt etwa 2 Sekunden, und bei einem Notfall-Suspend kommt die Benachrichtigung nicht einmal an.12
  • Beim Fortsetzen aus dem Suspend kommt PBT_APMRESUMEAUTOMATIC an, und bei einem vom Benutzer ausgelösten Fortsetzen kommt zusätzlich PBT_APMRESUMESUSPEND. Notwendige Arbeit wie das Wiederverbinden gehört in der Regel auf die Erstere. Übergänge in den und aus dem energiesparenden Leerlauf von Modern Standby richten sich allerdings nicht immer nach diesen Benachrichtigungen, behandeln Sie die Benachrichtigungen also als Hilfe.34
  • Entwerfen Sie unter der Annahme, dass TCP-Verbindungen, serielle Schnittstellen und Geräte-Handles das Fortsetzen nicht überleben. Wiederverbindungslogik, die sie bei einer Fortsetzungsbenachrichtigung oder einem Kommunikationsfehler neu aufbaut, ist die Hauptsache.
  • Achten Sie auf Timer und den Umgang mit der Zeit. Periodische Arbeit stoppt während des Schlafs, und wie sie unmittelbar nach dem Fortsetzen feuert, unterscheidet sich nach Timer-API und Laufzeit. Es gibt auch einen „riesigen Sprung der verstrichenen Zeit“, der sichere Ansatz ist daher, den Zeitplan beim Fortsetzen neu aufzubauen.
  • Unterdrücken Sie den Schlaf ausdrücklich für Intervalle, die Sie nicht verschlafen wollen. Nutzen Sie SetThreadExecutionState (ES_SYSTEM_REQUIRED) oder eine Power Request (PowerSetRequest), und heben Sie sie immer auf, wenn die Arbeit fertig ist.45
  • Auf einer Modern-Standby-Maschine läuft das System während des Schlafs noch intermittierend, aber Desktop-Apps sind pausiert. Sie können die Erwartung nicht halten, dass „unsere App während des Schlafs weiterlaufen sollte“.6
  • Die Standarduntersuchungswerkzeuge sind powercfg (/requests, /lastwake, /sleepstudy) und Kernel-Power im Ereignisprotokoll.

2. Was rund um den Schlaf passiert — Der Ablauf der Energieereignisse

Das Betriebssystem sendet Energiezustandsänderungen als WM_POWERBROADCAST-Nachricht an jede App.2 Es gibt drei Hauptereignisse, die Schlaf und Fortsetzen betreffen.

Ereignis Bedeutung
PBT_APMSUSPEND Im Begriff, in den Schlaf zu gehen (die letzte Chance zur Vorbereitung)
PBT_APMRESUMEAUTOMATIC Fortgesetzt (kommt beim Fortsetzen immer an)
PBT_APMRESUMESUSPEND Fortsetzen durch eine Benutzeraktion (dieses ist bedingt)

PBT_APMSUSPEND ist die Benachrichtigung kurz vor dem Schlaf, und hier können Sie sich vorbereiten, indem Sie Dateien schließen und den Zustand speichern. Es gibt allerdings zwei Bedingungen. Erstens: Die erlaubte Verarbeitungszeit beträgt etwa 2 Sekunden pro App, und wenn Sie sie überschreiten, macht das System ohne Warten weiter.1 Zweitens: Bei einem Notfall-Suspend wie kritisch niedrigem Akku schläft es sofort ohne Vorabbenachrichtigung.2 Ein Entwurf, der „vor dem Schlaf fertig sein muss“, hält nicht. Behandeln Sie die Benachrichtigung als Chance, „es zu tun, wenn Sie es schaffen“, und legen Sie die Hauptarbeit auf die Fortsetzungsseite.

Die Fortsetzungsseite hat zwei Stufen. PBT_APMRESUMEAUTOMATIC kommt beim Fortsetzen aus einem Suspend-Übergang an. Darauf folgt, wenn die Maschine wegen einer Benutzeraktion wie der Ein-/Aus-Taste oder einem Tastendruck fortgesetzt hat (oder anschließend Benutzerpräsenz erkannt wurde), PBT_APMRESUMESUSPEND. Umgekehrt liefert ein unbeaufsichtigtes Fortsetzen für ein Remote-Aufwachen über das Netz oder für Wartung nur PBT_APMRESUMEAUTOMATIC.3 Diese zwei Stufen sind selbst ein Hinweis, wie Sie die Arbeit aufteilen — machen Sie mechanische Wiederherstellung wie das Neuaufbauen von Verbindungen bei PBT_APMRESUMEAUTOMATIC, und machen Sie benutzerseitige Aktionen wie Bildschirmaktualisierungen oder eine erneute Anmeldeaufforderung bei PBT_APMRESUMESUSPEND.

Benachrichtigungsablauf für Schlaf und FortsetzenPBT_APMSUSPEND kommt kurz vor dem Schlaf mit etwa 2 Sekunden Schonfrist; beim Fortsetzen kommt PBT_APMRESUMEAUTOMATIC immer an, und PBT_APMRESUMESUSPEND folgt nur bei vom Benutzer ausgelöstem FortsetzenAppBetriebssystemAppBetriebssystemSchlaf (Code läuft nicht)PBT_APMSUSPEND (etwa 2 Sekunden Schonfrist)Zustand speichern und Verbindungen schließenPBT_APMRESUMEAUTOMATIC (kommt beim Fortsetzen an)Wiederverbinden und Zustand wiederherstellenPBT_APMRESUMESUSPEND (nur benutzergesteuertes Fortsetzen)Bildschirmaktualisierungen und andere benutzerseitige Arbeit

Abbildung 1: Benachrichtigungen sind nur „ein Wort kurz vorher und ein oder zwei Worte nach dem Fortsetzen“. Der Star der Wiederherstellung ist die Arbeit auf der Fortsetzungsseite.

Unterschied zwischen gewöhnlichem Schlaf und Notfall-SuspendGewöhnlicher Schlaf liefert kurz vorher PBT_APMSUSPEND mit etwa 2 Sekunden zur Vorbereitung, aber ein Notfall-Suspend wie kritischer Akku stoppt ohne Vorabbenachrichtigung, sodass ein Entwurf, der von der Vorabbenachrichtigung abhängt, nicht hältGewöhnlicher SchlafPBT_APMSUSPEND(etwa 2 Sekunden Schonfrist)Vorbereiten, dann stoppenNotfall-Suspend(kritisch niedriger Akku)Stoppen ohne VorabbenachrichtigungEin Entwurf, der die Benachrichtigung voraussetzt, hält nicht

Abbildung 2: Ein Notfall-Suspend kommt ohne Vorwarnung. Vorbereitung ist also „ein Bonus, wenn Sie es schaffen“, und die Hauptarbeit liegt auf der Fortsetzungsseite.

Beachten Sie, dass WM_POWERBROADCAST die Art des energiesparenden Zustands (Schlaf gegenüber Ruhezustand/Hibernate) nicht unterscheidet.4 Die richtige Abstraktion für die App ist, es als eine Art von Ereignis zu behandeln: „es hat gestoppt, und es ist zurückgekommen“. Fensterlose Dienste und Konsolen-Apps können dieselben Benachrichtigungen empfangen, indem sie RegisterSuspendResumeNotification in Callback-Form (DEVICE_NOTIFY_CALLBACK) nutzen.7

Arbeit auf die zwei Fortsetzungsstufen aufteilenLegen Sie mechanische Wiederherstellung wie Wiederverbinden auf PBT_APMRESUMEAUTOMATIC, das beim Fortsetzen ankommt; legen Sie benutzerseitige Arbeit wie Bildschirmaktualisierungen oder eine erneute Anmeldeaufforderung auf PBT_APMRESUMESUSPEND, das nur bei vom Benutzer ausgelöstem Fortsetzen ankommtPBT_APMRESUMEAUTOMATIC(beim Fortsetzen)Mechanische WiederherstellungPBT_APMRESUMESUSPEND(benutzergesteuertes Fortsetzen)Benutzerseitige ArbeitWiederverbinden und Handles neu öffnenBildschirmaktualisierungen und erneute Anmeldung

Abbildung 3: Die Letztere kommt bei einem unbeaufsichtigten Fortsetzen nicht an, also verpassen Sie notwendige Wiederherstellung, wenn Sie sie auf die Letztere legen.

3. Modern Standby — Die Bedeutung von „Schlaf“ hat sich geändert

Eine weitere moderne Tatsache, die Sie aufnehmen sollten, ist Modern Standby. Der traditionelle S3-Schlaf war ein einfaches Modell, das „das System als Ganzes stoppte“; Schlaf auf einer Modern-Standby-Maschine ist ein smartphone-ähnliches Modell, in dem das System nach dem Ausschalten des Bildschirms intermittierend weiterläuft.

Was hier für eine Geschäftsanwendung zählt, ist, dass Desktop-Apps vom Desktop Activity Moderator (DAM) in der ersten Stufe des Schlafeintritts pausiert werden.6 Das System selbst läuft von Zeit zu Zeit weiter, um das Netz oben zu halten und Benachrichtigungen zu empfangen, aber die Komponenten, die davon profitieren, sind solche, die an diesem Mechanismus teilnehmen — gewöhnlicher Desktop-App-Code läuft nicht. Aus Entwicklersicht ist die Schlussfolgerung für Modern Standby und für S3 also dieselbe — entwerfen Sie unter der Annahme, dass Ihr Code während des Schlafs nicht läuft.

Unterschied zwischen traditionellem Schlaf und Modern StandbyTraditioneller S3-Schlaf stoppt das System als Ganzes, während unter Modern Standby das System nach dem Ausschalten des Bildschirms noch intermittierend läuft. Desktop-Apps werden in beiden Fällen vom DAM pausiert, sodass der Code der App nicht läuftTraditioneller S3-Schlaf: das ganze System stopptDer Code der App läuft nichtModern Standby: das System läuft intermittierendDesktop-Apps werden vom DAM pausiert

Abbildung 4: Das Modell hat sich geändert, aber für eine Desktop-App ist die Schlussfolgerung dieselbe: „Sie können während des Schlafs nicht laufen“.

Eine weitere Vorsicht gilt, wie wenig Sie sich auf die Benachrichtigungen verlassen können. Unter Modern Standby richten sich Übergänge in den und aus dem energiesparenden Leerlauf nicht nach dem traditionellen Suspend-Übergang, und eine Verbindung kann bereits getrennt sein, ohne dass je eine Benachrichtigung ankam. Behandeln Sie die Fortsetzungsbenachrichtigung als Hilfe, und legen Sie Wiederverbindung, die durch Fehlererkennung ausgelöst wird (Kapitel 5), auf den Hauptwiederherstellungspfad.

Ein weiterer Unterschied ist das „rutschige“ Gefühl des Verhaltens. Das Erreichen der Tiefen des Schlafs ist gestuft, und der Zeitpunkt von Trennungen und Stopps ist nicht so scharf wie unter S3. Der Unterschied zwischen „der Bildschirm ist nur aus“ und „es hat geschlafen“ ist für den Benutzer ebenfalls schwer zu sehen, also müssen Sie bei einem Symptom bestätigen, „haben sie den Deckel geschlossen“ und „wie viele Minuten war es im Leerlauf“.

4. Was kaputtgeht — Klassische Symptome

Die TCP-Verbindung ist tot. Während des Schlafs behandeln Gegenseite, NAT und Firewalls Ihr Schweigen als Timeout und verwerfen die Verbindung. Schlimmer: Der Socket auf dieser Seite weiß nichts vom Fehler, also schlägt er erst fehl, wenn Sie nach dem Fortsetzen senden oder empfangen. Oder noch schlimmer: Ein Empfangswarten fehlerte überhaupt nie (deshalb brauchen Sie einen Keepalive). Datenbankverbindungen und WebSockets haben dieselbe Form.

Handles von seriellen Schnittstellen und USB-Geräten werden ungültig. Ein per USB verbundenes Gerät kann beim Fortsetzen so aussehen, als wäre es einmal „abgezogen und wieder eingesteckt“ worden, und das Handle, das Sie offen hatten, beginnt Fehler zurückzugeben. Das ist das typische Muster einer Gerätesteuerungs-App, die „nur nach dem Mittagessen einen Kommunikationsfehler bekommt“. Wiederverbindungsentwurf ist auch im Artikel zur seriellen Kommunikation behandelt.

Die Kontinuität der Zeit bricht. Timergetriebene Arbeit wie „alle 10 Sekunden pollen“ feuert während des Schlafs nicht. Wie sie unmittelbar nach dem Fortsetzen feuert (fällige Arbeit feuert einmal sofort, bis zur nächsten Periode passiert nichts und so weiter) unterscheidet sich nach der Timer-API und der Laufzeit, die Sie nutzen, also überlassen Sie den Umgang mit verpassten Ticks nicht implizitem Verhalten — der sichere Ansatz ist, den Zeitplan bei der Fortsetzungsbenachrichtigung neu aufzubauen. Außerdem werden Berechnungen der verstrichenen Zeit (die Differenz zum vorherigen Zeitstempel) plötzlich zu „8 Stunden wert“, und Durchschnittsberechnungen oder Timeout-Urteile brechen. Geplante Arbeit wie „jede Nacht um 2 Uhr ausführen“ läuft einfach nicht, wenn der PC zu dieser Zeit schläft (wecken Sie ihn bei Bedarf mit der Aufwachfunktion des Taskplaners).

Drei Formen, in denen die Kontinuität der Zeit brichtPeriodische Arbeit stoppt während des Schlafs und das Feuern nach dem Fortsetzen unterscheidet sich nach API, also bauen Sie den Zeitplan beim Fortsetzen neu auf; die Differenz zum vorherigen Zeitstempel wird nach dem Fortsetzen riesig, also schützen Sie sie; geplante Arbeit läuft nicht, wenn die Maschine schläft, also erwägen Sie das Aufwachen aus dem Schlaf des TaskplanersPeriodische Arbeit: stopptZeitplan neu aufbauenVerstrichene Zeit: explodiertAbnorme Differenzen schützenGeplant: nie gelaufenAufwachen aus dem Schlaf

Abbildung 5: Schreiben Sie Timer- und Zeitbehandlung unter der Annahme, dass „die Zeit springt“. Jede der drei Formen hat eine Art von Gegenmaßnahme.

Drei Dinge, die über den Schlaf hinweg kaputtgehenÜber den Schlaf hinweg ist eine TCP-Verbindung durch ein Timeout auf der Gegenseite verworfen, das Handle eines USB-Geräts ist als Wiederverbindung ungültig, und Arbeit, die auf verstrichener Zeit basiert, beobachtet einen riesigen Zeitsprung. Stellen Sie jedes mit Wiederverbinden, Neuöffnen und einem Differenzschutz wieder herSchlafintervallTCP: Gegenseite hat sie verworfenUSB oder verstrichene Zeit?USB: Handle ungültigVerstrichene Zeit: ein SprungErkennen + wiederverbindenGerät neu öffnenAbnorme Differenzen schützen

Abbildung 6: Was kaputtgeht, fällt in drei Familien — „Verbindungen“, „Handles“ und „Kontinuität der Zeit“ — und jede hat eine gesetzte Art der Wiederherstellung.

Erneute Authentifizierung an gemeinsam genutzten Ressourcen. Netzlaufwerke und VPNs müssen nach dem Fortsetzen oft neu aufgebaut werden, und es gibt ein „Starttal“ von wenigen bis mehreren zehn Sekunden unmittelbar nach dem Fortsetzen, in dem der Zugriff fehlschlägt. Es ist sicherer, nicht alles sofort nach dem Fortsetzen auf einmal zu wiederholen, sondern ein wenig zu warten und gestuft zu wiederholen.

5. Apps bauen, die das Fortsetzen überstehen

Das Prinzip ist eine Sache. Nehmen Sie an, dass „Verbindungen und Handles den Schlaf nicht überleben“, und strukturieren Sie die App so, dass Sie sich immer erholen können.

Erkennen Sie das Fortsetzen und stellen Sie wieder her. Wenn das WM_POWERBROADCAST des obersten Fensters PBT_APMRESUMEAUTOMATIC empfängt, verwerfen Sie die Verbindungen, die Sie halten, und bauen Sie sie neu auf. Der Punkt ist, sich nicht allein auf die Fortsetzungsbenachrichtigung zu verlassen. Verpasste Benachrichtigungen und Kommunikation, die vor der Benachrichtigung stattfindet, sind beide real, also paaren Sie sie immer mit einem Pfad, der „wiederverbindet, wenn ein Kommunikationsfehler erkannt wird“, und behandeln Sie die Fortsetzungsbenachrichtigung als Auslöser, der das lediglich früher startet.

// C#: funnel both the resume notification and communication errors into the same reconnect path
protected override void WndProc(ref Message m)
{
    const int WM_POWERBROADCAST = 0x0218;
    const int PBT_APMRESUMEAUTOMATIC = 0x0012;
    if (m.Msg == WM_POWERBROADCAST && (int)m.WParam == PBT_APMRESUMEAUTOMATIC)
    {
        _connectionManager.RequestReconnect();   // idempotent reconnect request
    }
    base.WndProc(ref m);
}

Machen Sie die Wiederverbindungsarbeit selbst idempotent (sicher, egal wie oft sie aufgerufen wird), wiederholen Sie bei Fehlschlag mit exponentiellem Backoff, und erkennen Sie im Dauerzustand eine tote Verbindung früh mit einem Keepalive — setzen Sie diese drei als Satz zusammen, und Sie überstehen nicht nur das Fortsetzen aus dem Schlaf, sondern auch einen kurzen Netzabbruch oder einen Geräteneustart.

Fortsetzungsfester WiederverbindungsentwurfDie Fortsetzungsbenachrichtigung, ein Kommunikationsfehler und ein Keepalive-Fehlschlag münden alle in dieselbe idempotente Wiederverbindungsarbeit, die bei Fehlschlag mit exponentiellem Backoff wiederholtjaneinFortsetzungsbenachrichtigung(PBT_APMRESUMEAUTOMATIC)Idempotente WiederverbindungsarbeitErkennung eines KommunikationsfehlersKeepalive-FehlschlagGelungen?Zurück in den NormalbetriebNach exponentiellem Backoff wiederholen

Abbildung 7: Konzentrieren Sie Wiederverbindung in einen einzigen idempotenten Pfad, und betreten Sie dieselbe Straße von der Fortsetzungsbenachrichtigung, der Fehlererkennung oder dem Keepalive.

Überdenken Sie den Umgang mit der Zeit. Für Arbeit, die „verstrichene Zeit seit dem letzten Mal“ nutzt, setzen Sie einen Schutz ein, der das Intervall ungültig macht, wenn er eine abnormal große Differenz erkennt (falten Sie sie nicht in einen Durchschnitt, behandeln Sie sie nicht als Timeout). Das Messen verstrichener Zeit über das Fortsetzen hinweg erfordert, eine Unterscheidung zwischen einer Uhr, die während des Schlafs weiterläuft (Wanduhrenzeit), und tatsächlich für Arbeit aufgewendeter Zeit zu halten.

Unterdrücken Sie den Schlaf ausdrücklich für Intervalle, die Sie nicht verschlafen wollen. Während Arbeit, die nicht verschlafen werden darf — eine Datenmigration, kontinuierliche Kommunikation mit einem Gerät und so weiter — können Sie das System mit SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED) wach halten (fügen Sie ES_DISPLAY_REQUIRED hinzu, wenn Sie auch den Bildschirm anlassen wollen).45 Eine besser erzogene Methode ist die Power-Request-API (PowerCreateRequest + PowerSetRequest), die eine Begründungszeichenfolge anhängen kann, und powercfg /requests zeigt dann „wer blockiert und warum“.8 Beachten Sie, dass Unterdrückung über SetThreadExecutionState pro Thread gilt und Sie sie von demselben Thread aufheben, der sie gesetzt hat. Für Arbeit, die Threads wechselt, etwa async/await, nutzen Sie die Power-Request-Seite, die über ein Handle verwaltet wird. Es gibt Vorsichten. Erstens: Was diese unterdrücken, ist automatischer Leerlaufschlaf. Sie können eine ausdrückliche Benutzeraktion wie das Schließen des Deckels oder die Wahl von Energie sparen im Startmenü nicht stoppen, also können Sie den Wiederverbindungsentwurf dieses Kapitels auch bei eingeschalteter Unterdrückung nicht überspringen. Zweitens: Am Akku auf einer Modern-Standby-Maschine werden diese Power Requests auch einige Zeit nach Ablauf des Schlaf-Timeouts abgeschnitten. Arbeit, die nicht unterbrochen werden darf, muss durch Netzbetrieb oder durch den Betrieb garantiert werden.8 Drittens: Heben Sie sie immer auf, wenn die Arbeit fertig ist. Ein verpasstes Aufheben wird zu einem neuen Fehler: „dieser PC schläft aus irgendeinem Grund nicht“.

Zwei Mittel, den Schlaf zu unterdrückenOb Sie das bequeme SetThreadExecutionState oder die Power-Request-API nutzen, die eine Begründungszeichenfolge anhängen kann und für einen Administrator über powercfg sichtbar ist — heben Sie sie immer auf, wenn die Arbeit endetArbeitsintervall, das nicht verschlafen werden darfSetThreadExecutionStatePower Request(PowerSetRequest)Bequem — nur FlagsMit Begründung — in powercfg sichtbarImmer aufheben, wenn die Arbeit endet

Abbildung 8: Für beide Mittel ist „aufheben, wenn Sie fertig sind“ eine absolute Bedingung. Eine Power Request, die die Begründung sichtbar machen kann, ist freundlicher zum Betrieb.

Dienste und fensterlose Apps empfangen Callback-Benachrichtigungen mit RegisterSuspendResumeNotification (DEVICE_NOTIFY_CALLBACK).7 Wenn Dauerbetrieb eine echte Anforderung ist, ist die Wurzelösung, einen Entwurf zu überdenken, der die Arbeit auf einem Client-PC hält, der schläft, und sie auf die Serverseite oder auf eine Maschine zu verschieben, die ohne Schlaf betrieben wird.

6. Untersuchung — powercfg und das Ereignisprotokoll

Untersuchungen rund um Energie sind gut von den Werkzeugen bedient, die mit dem Betriebssystem mitkommen.

  • Es schläft nicht: powercfg /requests listet die Prozesse und Treiber, die eine Power Request ausgestellt haben. „Die App hat vergessen, SetThreadExecutionState aufzuheben“ taucht hier auch auf.
  • Es wacht von selbst auf: powercfg /lastwake zeigt den jüngsten Aufwachgrund, und powercfg /waketimers zeigt Timer, die derzeit reserviert sind, um die Maschine aufzuwecken.
  • Modern-Standby-Qualität: powercfg /sleepstudy erzeugt einen Bericht über Stromverbrauch und Aktivität pro Schlafintervall.9
  • Die Zeitachse bestätigen: Die Quelle Kernel-Power im Ereignisprotokoll (System) hält Eintritte in den Schlaf und Fortsetzen fest. Wenn Sie sie mit dem Log der App abgleichen, können Sie objektiv bestätigen, ob „knapp vor dem Fehler ein Fortsetzen war“.
Abbildung von Energieproblem-Symptomen auf UntersuchungsbefehleBei einem Symptom „schläft nicht“ finden Sie mit powercfg /requests, wer eine Power Request hält; bei einem Symptom „wacht von selbst auf“ finden Sie den Aufwachgrund mit /lastwake und /waketimers; für eine Zeitachse nutzen Sie Kernel-Power im EreignisprotokollEs schläft nichtpowercfg /requestsEs wacht von selbst aufpowercfg /lastwake und /waketimersDie Zeitachse bestätigen wollenKernel-Power im EreignisprotokollEine vergessene Schlafunterdrückung taucht auch auf

Abbildung 9: Symptome bilden sich in drei Familien auf Untersuchungsbefehle ab. Bestätigen Sie zuerst „hat es gerade geschlafen“, dann teilen Sie auf.

In der Ticketbehandlung beschleunigt schon die erste Frage „hat der PC knapp vorher geschlafen (haben sie den Deckel geschlossen)“ die Isolation stark.

7. Zusammenfassung

  • Schlaf kann nicht abgelehnt werden. Die Vorabbenachrichtigung (PBT_APMSUSPEND) ist Best-Effort mit etwa 2 Sekunden Schonfrist, und in einem Notfall kommt sie nicht. Legen Sie den Hauptentwurf auf die Fortsetzungsseite.
  • Fortsetzungsbenachrichtigungen sind PBT_APMRESUMEAUTOMATIC (beim Fortsetzen aus dem Suspend) + PBT_APMRESUMESUSPEND (bei einer Benutzeraktion). Halten Sie fehlergetriebene Wiederverbindung auf dem Hauptpfad für den Fall, dass die Benachrichtigung nicht ankommt.
  • Nehmen Sie an, dass Verbindungen und Handles das Fortsetzen nicht überleben, und setzen Sie den Dreiteiler aus idempotenter Wiederverbindung + exponentiellem Backoff + einem Keepalive um.
  • Setzen Sie einen Schutz gegen „abnorme Differenzen“ auf Arbeit, die auf verstrichener Zeit basiert. Entwerfen Sie geplante Arbeit unter der Annahme, dass sie während des Schlafs nicht läuft.
  • Für Intervalle, die nicht verschlafen werden dürfen, unterdrücken Sie den Schlaf ausdrücklich mit SetThreadExecutionState oder einer Power Request, und heben Sie sie immer auf, wenn Sie fertig sind.
  • Untersuchung ist powercfg (/requests, /lastwake, /sleepstudy) und das Kernel-Power-Ereignisprotokoll. In der Ticketbehandlung fragen Sie zuerst „hat es knapp vorher geschlafen“.

Aus Sicht der App ist Schlaf ein Ereignis, in dem „die Zeit ohne Vorwarnung springt, Verbindungen zur Umgebung gekappt werden und es dann zurückkommt“. Ob Sie das als Teil des Alltags in den Entwurf eingewebt haben, statt als anomale Situation, ist das, was die Stabilität einer Geschäftsanwendung im Laptop-Zeitalter trennt.

Weiterführende Artikel

Zugehörige Beratungsfelder

KomuraSoft LLC übernimmt Ursachenuntersuchungen von Fehlern wie „die Kommunikation bricht nach dem Fortsetzen aus dem Schlaf“ und „die Verbindung zum Gerät fällt nach dem Mittagessen ab“, das nachträgliche Einbauen von Wiederverbindungslogik und Energieereignisbehandlung in bestehende Apps sowie Entwurfsreviews von Geschäftsanwendungen und Gerätesteuerungssoftware, die Laptop-Betrieb voraussetzen.

Quellen

  1. Microsoft Learn, PBT_APMSUSPEND event. Dazu, dass dies das Ereignis ist, das kurz bevor der Computer in den Suspend-Zustand geht ankommt; dazu, dass von der App erwartet wird, die zum Speichern von Daten nötige Arbeit zu beenden; sowie dazu, dass das System etwa 2 Sekunden zur Behandlung dieser Benachrichtigung erlaubt und eine App, die darüber hinaus fortsetzt, der Unterbrechung unterliegt.  2

  2. Microsoft Learn, System Power Management Events. Dazu, dass das System Betriebsmodusänderungen wie Schlaf im Voraus sendet; dazu, dass PBT_APMSUSPEND vor dem Leerlaufschlaf benachrichtigt wird, damit Sie sich durch Schließen von Dateien und Speichern von Daten vorbereiten können; dazu, dass ein Notfall-Suspend (kritischer Akku und Ähnliches) keine Vorabbenachrichtigung gibt; dazu, dass die Behandlung dieser Nachricht höchstens 2 Sekunden pro App erlaubt ist und nach dem Timeout abgeschnitten wird; sowie dazu, dass jede App beim Fortsetzen benachrichtigt wird.  2 3

  3. Microsoft Learn, PBT_APMRESUMESUSPEND event. Dazu, dass dies nach PBT_APMRESUMEAUTOMATIC bei einem vom Benutzer ausgelösten Fortsetzen oder wenn anschließend Benutzereingabe erkannt wird gesendet wird; dazu, dass für ein Fortsetzen aus einer äußeren Ursache wie einem Remote-Aufwachen nur PBT_APMRESUMEAUTOMATIC gesendet wird; sowie dazu, dass von der App erwartet wird, beim Schlaf geschlossene Dateien neu zu öffnen und sich auf Benutzereingabe vorzubereiten.  2

  4. Microsoft Learn, WM_POWERBROADCAST message. Dazu, dass PBT_APMRESUMEAUTOMATIC beim Fortsetzen immer gesendet wird und PBT_APMRESUMESUSPEND zusätzlich bei einem Fortsetzen aus Benutzereingabe; dazu, dass diese Nachricht die Art des energiesparenden Zustands nicht unterscheidet; dazu, dass Einzelheiten von Energiezustandsübergängen im Systemereignisprotokoll festgehalten werden; sowie dazu, SetThreadExecutionState aufzurufen, um zu verhindern, dass das System in einen energiesparenden Zustand geht.  2 3 4

  5. Microsoft Learn, SetThreadExecutionState function (winbase.h). Dazu, dass ES_SYSTEM_REQUIRED und ES_DISPLAY_REQUIRED den Leerlaufschlaf des Systems und das Ausschalten der Anzeige unterdrücken können; sowie dazu, dauerhafte Unterdrückung mit ES_CONTINUOUS zu erklären und sie durch den Aufruf von ES_CONTINUOUS allein aufzuheben, wenn Sie fertig sind.  2

  6. Microsoft Learn, Prepare software for modern standby. Dazu, dass der Desktop Activity Moderator (DAM) Desktop-Apps in der ersten Stufe des Übergangs in Modern Standby pausiert; sowie dazu, dass das System dann gestuft in eine energiesparende Phase und eine Resilienzphase übergeht, wobei nur erlaubte Komponenten intermittierend laufen.  2

  7. Microsoft Learn, RegisterSuspendResumeNotification function (winuser.h). Dazu, dass dies die API ist, die sich für Suspend-/Fortsetzungsbenachrichtigungen registriert, und dazu, DEVICE_NOTIFY_CALLBACK anzugeben, sodass eine fensterlose App oder ein Dienst die Benachrichtigung über einen Callback zusätzlich zur Nachrichtenzustellung an ein Fenster-Handle empfangen kann.  2

  8. Microsoft Learn, PowerSetRequest function (winbase.h). Dazu, dass Sie auf einem mit PowerCreateRequest erzeugten Power-Request-Objekt einen Anforderungstyp wie System- oder Anzeige-Wachhalten setzen können; dazu, dass Sie eine diagnostische Begründungszeichenfolge anhängen können; sowie dazu, dass ausstehende Power Requests mit powercfg /requests aufzählbar sind.  2

  9. Microsoft Learn, Modern standby SleepStudy. Dazu, dass der von powercfg /sleepstudy erzeugte Bericht Ihnen erlaubt, pro Modern-Standby-Intervall Stromverbrauch, Aktivität und den Aufwachgrund (Ein-/Aus-Taste, Benutzereingabe, ein Aufwach-Timer und so weiter) zu prüfen. 

Aktuelle Artikel mit denselben Schlagwörtern führen zu verwandten Themen weiter.

Diese Seiten ordnen den Artikel in einen größeren Leistungs- und Entscheidungskontext ein.

Dieser Artikel ist direkt mit den folgenden Leistungen verbunden.

Häufige Fragen

Fragen, die in Beratungen zu diesem Artikelthema häufig gestellt werden.

Kann eine App den Schlaf im Voraus erfahren und ihn ablehnen?
Unter aktuellem Windows können Sie die Benachrichtigung empfangen, aber nicht ablehnen. Kurz vor dem Schlaf liefert eine WM_POWERBROADCAST-Nachricht ein PBT_APMSUSPEND-Ereignis, und hier können Sie sich vorbereiten, indem Sie Dateien schließen und den Zustand speichern, aber die erlaubte Verarbeitungszeit beträgt etwa 2 Sekunden pro App, und wenn Sie sie überschreiten, macht das System ohne Warten weiter. Bei einem Notfall-Suspend, etwa bei kritisch niedrigem Akku, kommt die Vorabbenachrichtigung selbst nicht an. Ein Entwurf, der „vor dem Schlaf fertig sein muss“, hält also nicht; Sie brauchen einen Entwurf, der sich beim Fortsetzen erholen kann, egal wann der Schnitt kommt. Für eine Arbeitsstrecke, die Sie wirklich nicht verschlafen wollen, unterdrücken Sie den Schlaf ausdrücklich mit SetThreadExecutionState oder einer Power Request (PowerSetRequest).
Wie erkenne ich, dass die Maschine fortgesetzt hat?
Wenn die App ein Fenster hat, behandeln Sie WM_POWERBROADCAST. Beim Fortsetzen aus dem Suspend kommt PBT_APMRESUMEAUTOMATIC an, und wenn das Fortsetzen durch eine Benutzeraktion ausgelöst wurde (die Ein-/Aus-Taste oder ein Tastendruck), folgt PBT_APMRESUMESUSPEND. Ein unbeaufsichtigtes Fortsetzen, das sofort wieder schläft, liefert nur PBT_APMRESUMEAUTOMATIC, also liegt die Grundaufteilung darin, notwendige Arbeit wie das Wiederverbinden auf die Seite von PBT_APMRESUMEAUTOMATIC zu legen und benutzerseitige Arbeit wie Bildschirmaktualisierungen auf die Seite von PBT_APMRESUMESUSPEND. Fensterlose Dienste und Konsolen-Apps können dieselben Benachrichtigungen über einen Callback empfangen, indem Sie RegisterSuspendResumeNotification mit DEVICE_NOTIFY_CALLBACK verwenden.
Kann ich die App während des Schlafs weiterlaufen lassen?
In der Regel nein. Während des Schlafs stoppt die CPU-Ausführung selbst (auf einer Modern-Standby-Maschine werden Desktop-Apps vom Desktop Activity Moderator pausiert), und der Code der App läuft nicht. Es gibt zwei Wahlmöglichkeiten. Die eine ist, den Schlaf nur zu unterdrücken, solange Arbeit läuft. Die Angabe von ES_SYSTEM_REQUIRED mit SetThreadExecutionState oder das Ausstellen einer Power Request mit PowerCreateRequest/PowerSetRequest unterdrückt in diesem Intervall den automatischen Leerlaufschlaf (Sie können das mit powercfg /requests bestätigen). Das kann trotzdem eine ausdrückliche Schlafaktion wie das Schließen des Deckels durch den Benutzer nicht stoppen, also müssen Sie auch bei eingeschalteter Unterdrückung auf das Fortsetzen vorbereitet sein. Die andere ist, den Schlaf hinzunehmen und so zu entwerfen, dass Sie „nach dem Fortsetzen aufholen“. Für geplante Arbeit wie einen nächtlichen Batch können Sie den PC auch mit der Taskplaner-Funktion „Computer zum Ausführen dieser Aufgabe aktivieren“ aufwecken. Arbeit, die wirklich durchlaufen muss, gehört auf einen Server oder einen Dienst, der so konfiguriert ist, dass er nicht schläft.
Warum funktionieren TCP-Verbindungen und serielle Schnittstellen nach dem Fortsetzen nicht mehr?
Weil Netzadapter und USB-Geräte während des Schlafs ebenfalls in einen energiesparenden Zustand fallen. Die TCP-Verbindung ist auf der Gegenseite oder durch ein NAT- oder Firewall-Timeout bereits verworfen, und Senden/Empfangen nach dem Fortsetzen liefert einen Fehler (oft merken Sie es erst, wenn es fehlschlägt). USB-Seriell-Adapter und Ähnliches werden beim Fortsetzen manchmal als Geräteentfernung und Wiedereinsetzen behandelt, und das Handle, das Sie offen hatten, wird ungültig. Für beides ist die richtige Annahme, dass „Handles und Verbindungen das Fortsetzen nicht überleben“, und die richtige Antwort ist Wiederverbindungslogik, die die Verbindung bei einer Fortsetzungsbenachrichtigung oder einem Kommunikationsfehler neu aufbaut. Ein periodischer Keepalive kombiniert mit Wiederholungen, die bei Fehlschlag exponentielles Backoff nutzen, ist das etablierte Muster.
Wie untersuche ich unerwarteten Schlaf oder unerwartetes Fortsetzen?
Der Befehl powercfg ist das erste Werkzeug. In Richtung „es schläft nicht“ listet powercfg /requests, welche Prozesse und Treiber eine Power Request ausgestellt haben, die den Schlaf blockiert. In Richtung „es wacht von selbst auf“ zeigt powercfg /lastwake den jüngsten Aufwachgrund und powercfg /waketimers die Timer, die derzeit reserviert sind, um die Maschine aufzuwecken. Auf einer Modern-Standby-Maschine erzeugt powercfg /sleepstudy einen Bericht über Verbrauch und Aktivität während des Schlafs. Schlaf- und Fortsetzungsgeschichte ist auch im Ereignisprotokoll festgehalten (die Quelle Kernel-Power im Systemprotokoll), sodass Sie auf einer Zeitachse bestätigen können, „wann es geschlafen hat und wann und warum es aufgewacht ist“.

Autorenprofil

Profilseite des Artikelautors.

Go Komura

Geschäftsführer von KomuraSoft LLC

Spezialisiert auf Windows-Softwareentwicklung, technische Beratung und Fehleranalyse, insbesondere bei bestehenden Systemen und schwer reproduzierbaren Störungen.

Zurück zum Blog