Freigeben über


Asynchrone Datenträger-E/A wird unter Windows als synchron angezeigt

Dieser Artikel hilft Ihnen, das Problem zu beheben, bei dem das Standardverhalten für E/A synchron ist, aber es wird als asynchron angezeigt.

Originalproduktversion: Windows
Ursprüngliche KB-Nummer: 156932

Übersicht

Datei-E/A unter Microsoft Windows kann synchron oder asynchron sein. Das Standardverhalten für E/A ist synchron, wobei eine E/A-Funktion aufgerufen und zurückgegeben wird, wenn die E/A abgeschlossen ist. Asynchrone E/A-Funktion ermöglicht es einer E/A-Funktion, die Ausführung sofort wieder an den Aufrufer zurückzugeben, aber die E/A-Funktion wird bis zu einer späteren Zeit nicht abgeschlossen. Das Betriebssystem benachrichtigt den Aufrufer, wenn die E/A abgeschlossen ist. Stattdessen kann der Aufrufer den Status des ausstehenden E/A-Vorgangs mithilfe von Diensten des Betriebssystems ermitteln.

Der Vorteil asynchroner E/A-Vorgänge besteht darin, dass der Aufrufer Zeit hat, andere Aufgaben auszuführen oder mehr Anforderungen ausstellen zu können, während der E/A-Vorgang abgeschlossen wird. Der Begriff "Überlappte E/A" wird häufig für asynchrone E/A und nicht überlappende E/A für synchrone E/A verwendet. In diesem Artikel werden die Begriffe "Asynchron" und "Synchron" für E/A-Vorgänge verwendet. In diesem Artikel wird davon ausgegangen, dass der Leser mit den Datei-E/A-Funktionen wie CreateFile, ReadFile, WriteFilevertraut ist.

Häufig verhalten sich asynchrone E/A-Vorgänge genauso wie synchrone E/A-Vorgänge. Bestimmte Bedingungen, die in diesem Artikel in den späteren Abschnitten erläutert werden, wodurch die E/A-Vorgänge synchron abgeschlossen werden. Der Aufrufer hat keine Zeit für Die Hintergrundarbeit, da die E/A-Funktionen erst zurückgegeben werden, wenn die E/A abgeschlossen ist.

Mehrere Funktionen beziehen sich auf synchrone und asynchrone E/A. In diesem Artikel wird ReadFile und WriteFile als Beispiele verwendet. Gute Alternativen wären ReadFileEx und WriteFileEx. Obwohl in diesem Artikel nur die Datenträger-E/A speziell behandelt wird, können viele der Prinzipien auf andere Arten von E/A angewendet werden, z. B. serielle E/A oder Netzwerk-E/A.

Einrichten asynchroner E/A-Vorgänge

Das FILE_FLAG_OVERLAPPED Flag muss beim Öffnen der Datei angegeben CreateFile werden. Mit diesem Flag können E/A-Vorgänge für die Datei asynchron ausgeführt werden. Hier ist ein Beispiel:

HANDLE hFile;

hFile = CreateFile(szFileName,
                      GENERIC_READ,
                      0,
                      NULL,
                      OPEN_EXISTING,
                      FILE_FLAG_NORMAL | FILE_FLAG_OVERLAPPED,
                      NULL);

if (hFile == INVALID_HANDLE_VALUE)
      ErrorOpeningFile();

Achten Sie darauf, wenn Sie einen Code für asynchrone E/A-Vorgänge erstellen, da sich das System das Recht vorbehält, einen Vorgang synchron zu machen, wenn er erforderlich ist. Daher ist es am besten, wenn Sie das Programm schreiben, um einen E/A-Vorgang ordnungsgemäß zu behandeln, der entweder synchron oder asynchron abgeschlossen werden kann. Der Beispielcode veranschaulicht diese Berücksichtigung.

Es gibt viele Dinge, die ein Programm ausführen kann, während auf asynchrone Vorgänge gewartet wird, z. B. das Anwarteschlangen zusätzlicher Vorgänge oder das Ausführen von Hintergrundarbeiten. Der folgende Code behandelt z. B. den überlappenden und nicht überlappenden Abschluss eines Lesevorgangs ordnungsgemäß. Es tut nicht mehr als warten, bis die ausstehende E/A abgeschlossen ist:

if (!ReadFile(hFile,
               pDataBuf,
               dwSizeOfBuffer,
               &NumberOfBytesRead,
               &osReadOperation )
{
   if (GetLastError() != ERROR_IO_PENDING)
   {
      // Some other error occurred while reading the file.
      ErrorReadingFile();
      ExitProcess(0);
   }
   else
      // Operation has been queued and
      // will complete in the future.
      fOverlapped = TRUE;
}
else
   // Operation has completed immediately.
   fOverlapped = FALSE;

if (fOverlapped)
{
   // Wait for the operation to complete before continuing.
   // You could do some background work if you wanted to.
   if (GetOverlappedResult( hFile,
                           &osReadOperation,
                           &NumberOfBytesTransferred,
                           TRUE))
      ReadHasCompleted(NumberOfBytesTransferred);
   else
      // Operation has completed, but it failed.
      ErrorReadingFile();
}
else
   ReadHasCompleted(NumberOfBytesRead);

Notiz

&NumberOfBytesRead passed into ReadFile is different from &NumberOfBytesTransferred passed into GetOverlappedResult. Wenn ein Vorgang asynchron ausgeführt wurde, wird verwendet GetOverlappedResult , um die tatsächliche Anzahl der bytes zu ermitteln, die nach Abschluss des Vorgangs übertragen wurden. Das &NumberOfBytesRead übergebene ReadFile ist sinnlos.

Wenn ein Vorgang jedoch sofort abgeschlossen wird, ist der &NumberOfBytesRead übergebene ReadFile Vorgang für die Anzahl der gelesenen Bytes gültig. Ignorieren Sie in diesem Fall die OVERLAPPED übergebene ReadFileStruktur; verwenden Sie sie nicht mit GetOverlappedResult oder WaitForSingleObject.

Eine weitere Einschränkung bei asynchronen Vorgängen besteht darin, dass Sie eine OVERLAPPED Struktur erst verwenden dürfen, wenn der ausstehende Vorgang abgeschlossen ist. Mit anderen Worten: Wenn Sie über drei ausstehende E/A-Vorgänge verfügen, müssen Sie drei OVERLAPPED Strukturen verwenden. Wenn Sie eine OVERLAPPED Struktur wiederverwenden, erhalten Sie unvorhersehbare Ergebnisse in den E/A-Vorgängen, und Möglicherweise treten Datenbeschädigungen auf. Darüber hinaus müssen Sie sie ordnungsgemäß initialisieren, sodass sich keine überlasten Daten auf den neuen Vorgang auswirken, bevor Sie eine OVERLAPPED Struktur zum ersten Mal verwenden können oder bevor Sie sie wiederverwenden, nachdem ein früherer Vorgang abgeschlossen wurde.

Derselbe Einschränkungstyp gilt für den datenpuffer, der in einem Vorgang verwendet wird. Ein Datenpuffer darf erst gelesen oder geschrieben werden, wenn der entsprechende E/A-Vorgang abgeschlossen ist; Das Lesen oder Schreiben des Puffers kann Zu Fehlern und beschädigten Daten führen.

Asynchrone E/A scheint immer noch synchron zu sein

Wenn Sie die Anweisungen weiter oben in diesem Artikel befolgt haben, sind alle Ihre E/A-Vorgänge jedoch in der Regel synchron in der ausgegebenen Reihenfolge abgeschlossen, und keiner der ReadFile Vorgänge gibt FALSE mit GetLastError() der Rückgabe ERROR_IO_PENDINGzurück, was bedeutet, dass Sie keine Zeit für Hintergrundarbeiten haben. Warum geschieht dies?

Es gibt eine Reihe von Gründen, warum E/A-Vorgänge synchron abgeschlossen werden, auch wenn Sie für asynchronen Vorgang codiert haben.

Komprimierung

Eine Behinderung des asynchronen Vorgangs ist die NTFS-Komprimierung (New Technology File System). Der Dateisystemtreiber greift nicht asynchron auf komprimierte Dateien zu; Stattdessen werden alle Vorgänge synchron ausgeführt. Diese Behinderung gilt nicht für Dateien, die mit Hilfsprogrammen wie COMPRESS oder PKZIP komprimiert werden.

NTFS-Verschlüsselung

Ähnlich wie bei der Komprimierung bewirkt die Dateiverschlüsselung, dass der Systemtreiber asynchrone E/A in synchron konvertiert. Wenn die Dateien entschlüsselt werden, sind die E/A-Anforderungen asynchron.

Erweitern einer Datei

Ein weiterer Grund, warum E/A-Vorgänge synchron abgeschlossen werden, sind die Vorgänge selbst. Unter Windows ist jeder Schreibvorgang in eine Datei, die die Länge erweitert, synchron.

Notiz

Anwendungen können den zuvor erwähnten Schreibvorgang asynchron machen, indem sie die gültige Datenlänge der Datei mithilfe der SetFileValidData Funktion ändern und dann eine ausstellen WriteFile.

Mithilfe SetFileValidData von Anwendungen (die unter Windows XP und höheren Versionen verfügbar sind) können Anwendungen Dateien effizient erweitern, ohne dass eine Leistungseinbuße für die Füllung entsteht.

Da das NTFS-Dateisystem die Daten nicht bis zur gültigen Datenlänge (VDL) ausfüllt, die durch SetFileValidDatadefiniert wird, hat diese Funktion Sicherheitsauswirkungen, bei denen die Datei Clustern zugewiesen werden kann, die zuvor von anderen Dateien belegt wurden. Erfordert daher, SetFileValidData dass der Anrufer die neue SeManageVolumePrivilege Aktiviert hat (standardmäßig wird dies nur Administratoren zugewiesen). Microsoft empfiehlt, unabhängige Softwareanbieter (ISVs) sorgfältig die Auswirkungen der Verwendung dieser Funktion zu berücksichtigen.

cache

Die meisten E/A-Treiber (Datenträger, Kommunikation und andere) verfügen über Einen speziellen Fallcode, bei dem, wenn eine E/A-Anforderung sofort abgeschlossen werden kann, der Vorgang abgeschlossen wird und die ReadFile Funktion WriteFile WAHR zurückgibt. Auf alle Arten scheinen diese Arten von Vorgängen synchron zu sein. Bei einem Datenträgergerät kann eine E/A-Anforderung in der Regel sofort abgeschlossen werden, wenn die Daten im Arbeitsspeicher zwischengespeichert werden.

Daten befinden sich nicht im Cache.

Das Cacheschema kann jedoch für Sie verwendet werden, wenn sich die Daten nicht im Cache befinden. Der Windows-Cache wird intern mithilfe von Dateizuordnungen implementiert. Der Speicher-Manager in Windows bietet keinen asynchronen Seitenfehlermechanismus zum Verwalten der vom Cache-Manager verwendeten Dateizuordnungen. Der Cache-Manager kann überprüfen, ob sich die angeforderte Seite im Arbeitsspeicher befindet. Wenn Sie also einen asynchronen zwischengespeicherten Lesevorgang ausgeben und die Seiten nicht im Arbeitsspeicher enthalten sind, geht der Dateisystemtreiber davon aus, dass Der Thread nicht blockiert werden soll und die Anforderung von einem begrenzten Pool von Arbeitsthreads behandelt wird. Die Steuerung wird nach Ihrem ReadFile Anruf mit dem Lesevorgang noch ausstehend an Ihr Programm zurückgegeben.

Dies funktioniert gut für eine kleine Anzahl von Anforderungen, aber da der Pool von Arbeitsthreads begrenzt ist (derzeit drei auf einem 16-MB-System), werden immer noch nur einige Anforderungen an den Datenträgertreiber zu einem bestimmten Zeitpunkt in die Warteschlange gestellt. Wenn Sie zahlreiche E/A-Vorgänge für Daten ausstellen, die sich nicht im Cache befinden, werden der Cache-Manager und der Speicher-Manager gesättigt, und Ihre Anforderungen werden synchron ausgeführt.

Das Verhalten des Cache-Managers kann auch abhängig davon beeinflusst werden, ob Sie sequenziell oder zufällig auf eine Datei zugreifen. Die Vorteile des Caches werden am häufigsten beim sequenziellen Zugriff auf Dateien angezeigt. Das FILE_FLAG_SEQUENTIAL_SCAN Flag im CreateFile Aufruf optimiert den Cache für diesen Zugriffstyp. Wenn Sie jedoch zufällig auf Dateien zugreifen, verwenden Sie das FILE_FLAG_RANDOM_ACCESS Flag CreateFile , um den Cache-Manager anzuweisen, sein Verhalten für den zufälligen Zugriff zu optimieren.

Verwenden Sie den Cache nicht

Das FILE_FLAG_NO_BUFFERING Kennzeichen wirkt sich am meisten auf das Verhalten des Dateisystems für asynchronen Vorgang aus. Es ist die beste Möglichkeit, sicherzustellen, dass E/A-Anforderungen asynchron sind. Es weist das Dateisystem an, überhaupt keinen Cachemechanismus zu verwenden.

Notiz

Es gibt einige Einschränkungen für die Verwendung dieses Flags, die mit der Ausrichtung des Datenpuffers und der Größe des Gerätesektors zu tun haben. Weitere Informationen finden Sie in der Dokumentation zur ordnungsgemäßen Verwendung dieses Flags in der Dokumentation zur Funktion CreateFile.

Reale Testergebnisse

Nachfolgend sind einige Testergebnisse aus dem Beispielcode aufgeführt. Die Größe der Zahlen ist hier nicht wichtig und variiert von Computer zu Computer, aber die Beziehung der Zahlen im Vergleich zueinander beleuchtet den allgemeinen Effekt der Flags auf die Leistung.

Sie können davon ausgehen, dass die Ergebnisse ähnlich einer der folgenden Ergebnisse angezeigt werden:

  • Test 1

    Asynchronous, unbuffered I/O:  asynchio /f*.dat /n
    Operations completed out of the order in which they were requested.
       500 requests queued in 0.224264 second.
       500 requests completed in 4.982481 seconds.
    

    Dieser Test zeigt, dass das zuvor erwähnte Programm schnell 500 E/A-Anforderungen ausgestellt hat und viel Zeit hatte, andere Aufgaben zu erledigen oder weitere Anforderungen zu stellen.

  • Test 2

    Synchronous, unbuffered I/O: asynchio /f*.dat /s /n
        Operations completed in the order issued.
        500 requests queued and completed in 4.495806 seconds.
    

    Dieser Test zeigt, dass dieses Programm 4,495880 Sekunden auffordert, readFile zum Abschließen seiner Vorgänge aufzurufen, aber der Test 1 verbrachte nur 0,224264 Sekunden, um dieselben Anforderungen auszustellen. In Test 2 gab es keine zusätzliche Zeit für das Programm, hintergrundarbeiten zu erledigen.

  • Test 3

    Asynchronous, buffered I/O: asynchio /f*.dat
        Operations completed in the order issued.
        500 requests issued and completed in 0.251670 second.
    

    Dieser Test veranschaulicht die synchrone Art des Caches. Alle Lesevorgänge wurden in 0.251670 Sekunde ausgestellt und abgeschlossen. Mit anderen Worten, asynchrone Anforderungen wurden synchron abgeschlossen. Dieser Test veranschaulicht auch die hohe Leistung des Cache-Managers, wenn sich Daten im Cache befinden.

  • Test 4

    Synchronous, buffered I/O: asynchio /f*.dat /s
        Operations completed in the order issued.
        500 requests and completed in 0.217011 seconds.
    

    Dieser Test veranschaulicht die gleichen Ergebnisse wie bei Test 3. Synchrone Lesevorgänge aus dem Cache sind etwas schneller als asynchrone Lesevorgänge aus dem Cache. Dieser Test veranschaulicht auch die hohe Leistung des Cache-Managers, wenn sich Daten im Cache befinden.

Zusammenfassung

Sie können entscheiden, welche Methode am besten geeignet ist, da sie alle vom Typ, der Größe und der Anzahl von Vorgängen abhängig sind, die Ihr Programm ausführt.

Der Standarddateizugriff ohne Angabe spezieller Flags CreateFile ist ein synchroner und zwischengespeicherter Vorgang.

Notiz

Sie erhalten in diesem Modus ein gewisses automatisches asynchrones Verhalten, da der Dateisystemtreiber asynchrones Lesen vorausschauend und asynchrones Schreiben geänderter Daten ausführt. Obwohl dieses Verhalten die E/A-A-Anwendung nicht asynchron macht, ist es der ideale Fall für die überwiegende Mehrheit einfacher Anwendungen.

Wenn Ihre Anwendung jedoch nicht einfach ist, müssen Sie möglicherweise einige Profilerstellungs- und Leistungsüberwachung durchführen, um die beste Methode zu ermitteln, ähnlich wie die weiter oben in diesem Artikel gezeigten Tests. Das Profilieren der für die ReadFile Funktion WriteFile aufgewendeten Zeit und anschließendes Vergleichen dieser Zeit mit der Dauer der tatsächlichen E/A-Vorgänge ist nützlich. Wenn der Großteil der Zeit in der tatsächlichen Ausgabe der E/A aufgewendet wird, wird Ihre E/A synchron abgeschlossen. Wenn die Ausgabe von E/A-Anforderungen jedoch relativ klein ist, verglichen mit der Zeit, die für den Abschluss der E/A-Vorgänge benötigt wird, werden Ihre Vorgänge asynchron behandelt. Der weiter oben in diesem Artikel erwähnte Beispielcode verwendet die QueryPerformanceCounter Funktion, um eine eigene interne Profilerstellung auszuführen.

Die Leistungsüberwachung kann dabei helfen, zu bestimmen, wie effizient Ihr Programm den Datenträger und den Cache verwendet. Das Nachverfolgen eines der Leistungsindikatoren für das Cacheobjekt gibt die Leistung des Cache-Managers an. Das Nachverfolgen der Leistungsindikatoren für die Physischen Datenträger- oder logischen Datenträgerobjekte gibt die Leistung der Datenträgersysteme an.

Es gibt mehrere Dienstprogramme, die bei der Leistungsüberwachung hilfreich sind. PerfMon und DiskPerf sind besonders nützlich. Damit das System Daten zur Leistung der Datenträgersysteme sammelt, müssen Sie zuerst den DiskPerf Befehl ausstellen. Nachdem Sie den Befehl ausgegeben haben, müssen Sie das System neu starten, um die Datensammlung zu starten.

References

Synchrone und asynchrone E/A