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

Dieser Artikel hilft Ihnen bei der Behebung des Problems, bei dem das Standardverhalten für E/A synchron ist, aber als asynchron angezeigt wird.

              Originalversion des Produkts: Windows
Ursprüngliche KB-Nummer: 156932

Zusammenfassung

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 wird und zurückgibt, wenn die E/A abgeschlossen ist. Asynchrone E/A ermöglicht es einer E/A-Funktion, die Ausführung sofort wieder an den Aufrufer zurückzugeben, aber die E/A wird erst zu einem späteren Zeitpunkt als abgeschlossen angenommen. Das Betriebssystem benachrichtigt den Aufrufer, wenn die E/A abgeschlossen ist. Stattdessen kann der Aufrufer die status des ausstehenden E/A-Vorgangs mithilfe von Diensten des Betriebssystems ermitteln.

Der Vorteil asynchroner E/A 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 Überlappende 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, ReadFileund 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 Hintergrundarbeiten, da die E/A-Funktionen erst zurückgegeben werden, wenn die E/A-Vorgänge abgeschlossen sind.

Mehrere Funktionen stehen im Zusammenhang mit synchronen und asynchronen E/A-Vorgängen. In diesem Artikel werden und WriteFile als Beispiele verwendetReadFile. Gute Alternativen wären ReadFileEx und WriteFileEx. Obwohl in diesem Artikel nur spezifisch Datenträger-E/A behandelt wird, können viele der Prinzipien auf andere E/A-Typen wie serielle E/A oder Netzwerk-E/A angewendet werden.

Einrichten asynchroner E/A-Vorgänge

Das FILE_FLAG_OVERLAPPED Flag muss in CreateFile angegeben werden, wenn die Datei geöffnet wird. Dieses Flag ermöglicht die asynchrone Ausführung von E/A-Vorgängen für die Datei. Hier 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();

Seien Sie beim Programmieren für asynchrone E/A vorsichtig, da sich das System das Recht vorbehält, einen Vorgang bei Bedarf synchron auszuführen. Daher ist es am besten, wenn Sie das Programm schreiben, um einen E/A-Vorgang ordnungsgemäß zu verarbeiten, der entweder synchron oder asynchron abgeschlossen werden kann. Dieser Aspekt wird im Beispielcode veranschaulicht.

Es gibt viele Dinge, die ein Programm beim Warten auf den Abschluss asynchroner Vorgänge ausführen kann, z. B. das Anstellen zusätzlicher Vorgänge oder das Ausführen von Hintergrundarbeiten. Der folgende Code behandelt beispielsweise den überlappenden und nicht überlappenden Abschluss eines Lesevorgangs ordnungsgemäß. Sie führt nichts anderes aus, als auf den Abschluss der ausstehenden E/A zu warten:

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

Hinweis

&NumberOfBytesRead Übergeben in ReadFile unterscheidet sich von der &NumberOfBytesTransferred Übergabe an GetOverlappedResult. Wenn ein Vorgang asynchron wurde, wird verwendet, GetOverlappedResult um die tatsächliche Anzahl von Bytes zu bestimmen, die nach Abschluss des Vorgangs übertragen wurden. Das &NumberOfBytesRead übergebene ReadFile ist bedeutungslos.

Wenn ein Vorgang hingegen sofort abgeschlossen wird, gilt der &NumberOfBytesRead übergebene in ReadFile für die Anzahl der gelesenen Bytes. Ignorieren Sie in diesem Fall die OVERLAPPED an ReadFileübergebene -Struktur, verwenden Sie sie nicht mit GetOverlappedResult oder WaitForSingleObject.

Ein weiterer Nachteil bei asynchronen Vorgängen ist, dass Sie eine OVERLAPPED Struktur erst verwenden dürfen, wenn deren ausstehender Vorgang abgeschlossen ist. Anders ausgedrückt: 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 bei den E/A-Vorgängen, und es kann zu Datenbeschädigungen kommen. Darüber hinaus müssen Sie sie ordnungsgemäß initialisieren, sodass sich keine datenverknutzten 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 Fehler und beschädigte Daten verursachen.

Asynchrone E/A scheint weiterhin synchron zu sein

Wenn Sie jedoch die Anweisungen weiter oben in diesem Artikel befolgt haben, werden alle E/A-Vorgänge in der Regel immer noch synchron in der ausgegebenen Reihenfolge abgeschlossen, und keiner der ReadFile Vorgänge gibt FALSE zurück GetLastError()ERROR_IO_PENDING, 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 asynchrone Vorgänge codiert haben.

Komprimierung

Ein Hindernis für den asynchronen Vorgang 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 sind.

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 dafür, dass E/A-Vorgänge synchron abgeschlossen werden, sind die Vorgänge selbst. Unter Windows erfolgt jeder Schreibvorgang in eine Datei, die deren Länge verlängert, synchron.

Hinweis

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 einen WriteFileausgeben.

Mit SetFileValidData (die unter Windows XP und höheren Versionen verfügbar ist) können Anwendungen Dateien effizient erweitern, ohne dass eine Leistungseinbuße entsteht, wenn sie nicht ausgefüllt werden.

Da das NTFS-Dateisystem die Daten nicht bis zur gültigen Datenlänge (VDL) auffüllt, die durch SetFileValidDatadefiniert ist, hat diese Funktion Auswirkungen auf die Sicherheit, wenn der Datei möglicherweise Cluster zugewiesen werden, die zuvor von anderen Dateien belegt wurden. Erfordert daher, SetFileValidData dass für den Aufrufer das neue SeManageVolumePrivilege aktiviert ist (standardmäßig wird dies nur Administratoren zugewiesen). Microsoft empfiehlt unabhängigen Softwareanbietern (ISVs), die Auswirkungen einer solchen Funktion sorgfältig zu prüfen.

Cache

Die meisten E/A-Treiber (Datenträger, Kommunikation usw.) verfügen über einen speziellen Fallcode, bei dem der Vorgang abgeschlossen wird, wenn eine E/A-Anforderung sofort abgeschlossen werden kann, und die ReadFile Funktion oder WriteFilegibt TRUE zurück. In jeder Hinsicht 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 funktionieren, 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 Fehlermechanismus für Seiten, um die vom Cache-Manager verwendeten Dateizuordnungen zu verwalten. Der Cache-Manager kann überprüfen, ob sich die angeforderte Seite im Arbeitsspeicher befindet. Wenn Sie also einen asynchronen zwischengespeicherten Lesevorgang ausgeben und sich die Seiten nicht im Arbeitsspeicher befinden, geht der Dateisystemtreiber davon aus, dass Der Thread nicht blockiert werden soll, und die Anforderung wird von einem begrenzten Pool von Arbeitsthreads verarbeitet. Die Steuerung wird nach ihrem ReadFile Aufruf an Ihr Programm zurückgegeben, wobei der Lesevorgang noch aussteht.

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 wenige Anforderungen an den Datenträgertreiber zu einem bestimmten Zeitpunkt in die Warteschlange gestellt. Wenn Sie zahlreiche E/A-Vorgänge für Daten ausgeben, die sich nicht im Cache befinden, werden der Cache-Manager und der Speicher-Manager ausgelastet, und Ihre Anforderungen werden synchron ausgeführt.

Das Verhalten des Cache-Managers kann auch beeinflusst werden, je nachdem, ob Sie sequenziell oder zufällig auf eine Datei zugreifen. Die Vorteile des Caches werden am häufigsten beim sequenziellen Zugriff auf Dateien erkannt. Das FILE_FLAG_SEQUENTIAL_SCAN Flag im CreateFile Aufruf optimiert den Cache für diese Zugriffsart. Wenn Sie jedoch zufällig auf Dateien zugreifen, verwenden Sie das FILE_FLAG_RANDOM_ACCESS Flag in 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 Flag hat die meisten Auswirkungen auf das Verhalten des Dateisystems für asynchrone Vorgänge. Dies ist die beste Möglichkeit, um sicherzustellen, dass E/A-Anforderungen asynchron sind. Es weist das Dateisystem an, überhaupt keinen Cachemechanismus zu verwenden.

Hinweis

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

Reale Testergebnisse

Im Folgenden 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 die allgemeine Auswirkung der Flags auf die Leistung.

Sie können davon ausgehen, dass Ergebnisse ähnlich einer der folgenden 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 ausgegeben 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 damit verbracht hat, ReadFile aufzurufen, um seine Vorgänge abzuschließen, aber der Test 1 hat nur 0,224264 Sekunden benötigt, um die gleichen Anforderungen auszugeben. In Test 2 gab es keine zusätzliche Zeit für das Programm, um Hintergrundaufgaben 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 Natur des Caches. Alle Lesevorgänge wurden ausgegeben und in 0,251670 Sekunden abgeschlossen. Mit anderen Worten, asynchrone Anforderungen wurden synchron abgeschlossen. Dieser Test zeigt 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 zeigt die gleichen Ergebnisse wie in Test 3. Synchrone Lesevorgänge aus dem Cache werden etwas schneller abgeschlossen als asynchrone Lesevorgänge aus dem Cache. Dieser Test zeigt 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 alles vom Typ, der Größe und der Anzahl der Vorgänge abhängt, die ihr Programm ausführt.

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

Hinweis

Sie erhalten in diesem Modus ein gewisses automatisches asynchrones Verhalten, da der Dateisystemtreiber asynchrones Lesen und asynchrones verzögertes Schreiben geänderter Daten vorhersagt. Obwohl dieses Verhalten die E/A-Vorgänge der Anwendung nicht asynchron macht, ist es der ideale Fall für die überwiegende Mehrheit der einfachen Anwendungen.

Wenn Ihre Anwendung jedoch nicht einfach ist, müssen Sie möglicherweise eine Profilerstellung und Leistungsüberwachung durchführen, um die beste Methode zu ermitteln, ähnlich wie bei den weiter oben in diesem Artikel beschriebenen Tests. Es ist nützlich, die für die ReadFile Funktion oder WriteFile aufgewendete Zeit zu profilieren und dann zu vergleichen, wie lange es dauert, bis die tatsächlichen E/A-Vorgänge abgeschlossen sind. Wenn die meiste Zeit mit der tatsächlichen Ausgabe der E/A verbracht wird, werden Ihre E/A-Vorgänge synchron abgeschlossen. Wenn der Zeitaufwand für die Ausgabe von E/A-Anforderungen im Vergleich zu der Zeit, die zum Abschließen der E/A-Vorgänge benötigt wird, relativ gering ist, 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. Wenn Sie einen der Leistungsindikatoren für das Cache-Objekt nachverfolgen, wird die Leistung des Cache-Managers angezeigt. Das Nachverfolgen der Leistungsindikatoren für die Objekte "Physischer Datenträger" oder "Logischer Datenträger" zeigt die Leistung der Datenträgersysteme an.

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

References

Synchrone und asynchrone E/A