Asynchroniczne operacje we/wy dysku są wyświetlane jako synchroniczne w systemie Windows
Ten artykuł pomaga rozwiązać problem polegający na tym, że domyślne zachowanie we/wy jest synchroniczne, ale jest ono wyświetlane jako asynchroniczne.
Oryginalna wersja produktu: Windows
Oryginalny numer KB: 156932
Podsumowanie
Operacje we/wy plików w systemie Microsoft Windows mogą być synchroniczne lub asynchroniczne. Domyślne zachowanie we/wy jest synchroniczne, w którym wywoływana jest funkcja we/wy i zwracana po zakończeniu operacji we/wy. Asynchroniczne operacje we/wy umożliwiają natychmiastowe zwrócenie wykonania operacji we/wy do wywołującego, ale nie zakłada się, że operacje we/wy zostaną ukończone do czasu przyszłego. System operacyjny powiadamia obiekt wywołujący po zakończeniu operacji we/wy. Zamiast tego obiekt wywołujący może określić stan zaległej operacji we/wy przy użyciu usług systemu operacyjnego.
Zaletą asynchronicznego we/wy jest to, że obiekt wywołujący ma czas na wykonanie innej pracy lub wystawianie większej liczby żądań podczas wykonywania operacji we/wy. Termin Nakładające się operacje we/wy są często używane w przypadku asynchronicznych operacji we/wy i nienakładanych we/wy synchronicznych operacji we/wy. W tym artykule użyto terminów Asynchroniczne i Synchroniczne dla operacji we/wy. W tym artykule założono, że czytelnik ma znajomość funkcji we/wy plików, takich jak CreateFile
, , ReadFile
WriteFile
.
Często asynchroniczne operacje we/wy zachowują się tak samo jak synchroniczne operacje we/wy. Niektóre warunki omówione w kolejnych sekcjach, co sprawia, że operacje we/wy są wykonywane synchronicznie. Obiekt wywołujący nie ma czasu na pracę w tle, ponieważ funkcje we/wy nie zwracają się do momentu ukończenia operacji we/wy.
Kilka funkcji jest powiązanych z synchronicznymi i asynchronicznymi operacjami we/wy. W tym artykule użyto elementów ReadFile
i WriteFile
jako przykładów. Dobrymi alternatywami byłyby ReadFileEx
i WriteFileEx
. Mimo że w tym artykule omówiono tylko we/wy dysku, wiele zasad można zastosować do innych typów we/wy, takich jak szeregowe we/wy lub sieciowe we/wy.
Konfigurowanie asynchronicznego we/wy
Flaga musi być określona FILE_FLAG_OVERLAPPED
w CreateFile
pliku po otwarciu pliku. Ta flaga umożliwia asynchroniczne wykonywanie operacji we/wy w pliku. Oto przykład:
HANDLE hFile;
hFile = CreateFile(szFileName,
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_FLAG_NORMAL | FILE_FLAG_OVERLAPPED,
NULL);
if (hFile == INVALID_HANDLE_VALUE)
ErrorOpeningFile();
Należy zachować ostrożność podczas kodowania asynchronicznego we/wy, ponieważ system zastrzega sobie prawo do synchronicznego wykonania operacji, jeśli jest to konieczne. Dlatego najlepiej jest napisać program, aby poprawnie obsługiwać operację we/wy, którą można wykonać synchronicznie lub asynchronicznie. Przykładowy kod demonstruje tę kwestie.
Istnieje wiele rzeczy, które program może wykonać podczas oczekiwania na zakończenie operacji asynchronicznych, takich jak kolejkowanie dodatkowych operacji lub wykonywanie pracy w tle. Na przykład poniższy kod prawidłowo obsługuje nakładające się i nienakładane ukończenie operacji odczytu. Nie robi to nic więcej niż czekać na ukończenie wybitnych operacji we/wy:
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);
Uwaga 16.
&NumberOfBytesRead
przekazana do ReadFile
elementu różni się od &NumberOfBytesTransferred
przekazywanego do GetOverlappedResult
elementu . Jeśli operacja została wykonana asynchronicznie, GetOverlappedResult
zostanie użyta do określenia rzeczywistej liczby bajtów przeniesionych w operacji po jej zakończeniu. &NumberOfBytesRead
Przekazany do ReadFile
jest bezsensowny.
Z drugiej strony, jeśli operacja zostanie ukończona natychmiast, przekazanie &NumberOfBytesRead
ReadFile
do elementu jest prawidłowe dla liczby odczytanych bajtów. W takim przypadku zignoruj OVERLAPPED
strukturę przekazaną do ReadFile
elementu ; nie używaj jej z elementem GetOverlappedResult
lub WaitForSingleObject
.
Innym zastrzeżeniem operacji asynchronicznej jest to, że nie można użyć OVERLAPPED
struktury, dopóki nie zakończy się jego oczekująca operacja. Innymi słowy, jeśli masz trzy wybitne operacje we/wy, musisz użyć trzech OVERLAPPED
struktur. Jeśli ponownie użyjesz OVERLAPPED
struktury, otrzymasz nieprzewidywalne wyniki operacji we/wy i może wystąpić uszkodzenie danych. Ponadto należy je poprawnie zainicjować, aby żadne dane pozostawione nie wpływały na nową operację, zanim będzie można użyć struktury po raz pierwszy lub przed ponownym użyciem OVERLAPPED
jej po zakończeniu wcześniejszej operacji.
Ten sam typ ograniczenia dotyczy buforu danych używanego w operacji. Bufor danych nie może być odczytywany ani zapisywany do momentu zakończenia odpowiedniej operacji we/wy; odczytywanie lub zapisywanie buforu może powodować błędy i uszkodzone dane.
Asynchroniczne operacje we/wy nadal wydają się być synchroniczne
Jeśli jednak wykonano instrukcje opisane wcześniej w tym artykule, wszystkie operacje we/wy są nadal zwykle wykonywane synchronicznie w wydanej kolejności, a żadna z ReadFile
operacji nie zwraca wartości FALSE z GetLastError()
zwracaniem ERROR_IO_PENDING
wartości , co oznacza, że nie masz czasu na żadną pracę w tle. Dlaczego tak się dzieje?
Istnieje wiele powodów, dla których operacje we/wy kończą się synchronicznie, nawet jeśli kodowane dla operacji asynchronicznej.
Kompresja
Jedną z przeszkód dla operacji asynchronicznej jest kompresja systemu plików New Technology (NTFS). Sterownik systemu plików nie będzie uzyskiwać dostępu do skompresowanych plików asynchronicznie; zamiast tego wszystkie operacje są wykonywane synchronicznie. Ta przeszkoda nie ma zastosowania do plików skompresowanych za pomocą narzędzi podobnych do COMPRESS lub PKZIP.
Szyfrowanie NTFS
Podobnie jak kompresja, szyfrowanie plików powoduje, że sterownik systemowy konwertuje asynchroniczne operacje we/wy na synchroniczne. Jeśli pliki zostaną odszyfrowane, żądania we/wy będą asynchroniczne.
Rozszerzanie pliku
Innym powodem, dla którego operacje we/wy są wykonywane synchronicznie, są same operacje. W systemie Windows każda operacja zapisu w pliku, który wydłuża jego długość, będzie synchroniczna.
Uwaga 16.
Aplikacje mogą asynchroniczne wykonać wcześniej wymienioną operację zapisu, zmieniając prawidłową długość danych pliku przy użyciu SetFileValidData
funkcji , a następnie wydając element WriteFile
.
Za pomocą SetFileValidData
funkcji (dostępnej w systemie Windows XP i nowszych wersjach) aplikacje mogą wydajnie rozszerzać pliki bez ponoszenia kary za wydajność bez ich wypełniania.
Ponieważ system plików NTFS nie wypełnia danych do prawidłowej długości danych (VDL), która jest zdefiniowana przez SetFileValidData
program , ta funkcja ma wpływ na zabezpieczenia, w których plik może być przypisany do klastrów, które były wcześniej zajmowane przez inne pliki. SetFileValidData
W związku z tym program wywołujący ma włączone nowe SeManageVolumePrivilege
ustawienie (domyślnie jest to przypisane tylko do administratorów). Firma Microsoft zaleca, aby niezależni dostawcy oprogramowania starannie rozważyli implikacje korzystania z takiej funkcji.
Pamięć podręczna
Większość sterowników we/wy (dysku, komunikacji i innych) ma specjalny kod przypadku, w którym jeśli żądanie we/wy może zostać ukończone natychmiast, operacja zostanie ukończona, a ReadFile
funkcja or WriteFile
zwróci wartość TRUE. Na wszystkie sposoby te typy operacji wydają się być synchroniczne. W przypadku urządzenia dyskowego zazwyczaj żądanie we/wy można wykonać natychmiast, gdy dane są buforowane w pamięci.
Dane nie są w pamięci podręcznej
Jednak schemat pamięci podręcznej może działać przeciwko Tobie, jeśli dane nie są w pamięci podręcznej. Pamięć podręczna systemu Windows jest implementowana wewnętrznie przy użyciu mapowań plików. Menedżer pamięci w systemie Windows nie zapewnia asynchronicznego mechanizmu błędów strony do zarządzania mapowaniami plików używanymi przez menedżera pamięci podręcznej. Menedżer pamięci podręcznej może sprawdzić, czy żądana strona jest w pamięci, więc jeśli wydasz asynchroniczną buforowaną operację odczytu, a strony nie znajdują się w pamięci, sterownik systemu plików zakłada, że nie chcesz, aby wątek został zablokowany, a żądanie zostanie obsłużone przez ograniczoną pulę wątków roboczych. Kontrolka jest zwracana do programu po ReadFile
wywołaniu z odczytem nadal oczekującym.
Działa to dobrze w przypadku niewielkiej liczby żądań, ale ponieważ pula wątków roboczych jest ograniczona (obecnie trzy w systemie 16 MB), nadal będzie tylko kilka żądań w kolejce do sterownika dysku w określonym czasie. Jeśli wydasz wiele operacji we/wy dla danych, które nie znajdują się w pamięci podręcznej, menedżer pamięci podręcznej i menedżer pamięci staną się nasycone, a żądania są wykonywane synchronicznie.
Zachowanie menedżera pamięci podręcznej może mieć również wpływ na to, czy uzyskujesz dostęp do pliku sekwencyjnie, czy losowo. Zalety pamięci podręcznej są widoczne najczęściej podczas uzyskiwania dostępu do plików sekwencyjnie. Flaga FILE_FLAG_SEQUENTIAL_SCAN
wywołania CreateFile
zoptymalizuje pamięć podręczną dla tego typu dostępu. Jeśli jednak uzyskujesz dostęp do plików w sposób losowy, użyj FILE_FLAG_RANDOM_ACCESS
flagi w programie CreateFile
, aby poinstruować menedżera pamięci podręcznej, aby zoptymalizować jego zachowanie pod kątem dostępu losowego.
Nie używaj pamięci podręcznej
Flaga FILE_FLAG_NO_BUFFERING
ma największy wpływ na zachowanie systemu plików dla operacji asynchronicznej. Jest to najlepszy sposób zagwarantowania, że żądania we/wy są asynchroniczne. Nakazuje systemowi plików, aby w ogóle nie używał żadnego mechanizmu pamięci podręcznej.
Uwaga 16.
Istnieją pewne ograniczenia dotyczące używania tej flagi, które muszą mieć związek z wyrównaniem buforu danych i rozmiarem sektora urządzenia. Aby uzyskać więcej informacji, zobacz dokumentację funkcji w dokumentacji funkcji CreateFile dotyczącą prawidłowego używania tej flagi.
Wyniki testów w świecie rzeczywistym
Poniżej przedstawiono kilka wyników testu z przykładowego kodu. Wielkość liczb nie jest tutaj ważna i różni się od komputera do komputera, ale relacja liczb w porównaniu ze sobą oświetla ogólny wpływ flag na wydajność.
Możesz oczekiwać, że wyniki będą podobne do jednego z następujących:
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.
Ten test pokazuje, że wcześniej wymieniony program szybko wystawił 500 żądań we/wy i miał dużo czasu na wykonywanie innych czynności lub wystawianie większej liczby żądań.
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.
Ten test pokazuje, że ten program spędził 4,495880 sekund wywołując plik ReadFile w celu ukończenia operacji, ale test 1 spędził tylko 0,224264 sekundy, aby wysyłać te same żądania. W teście 2 nie było dodatkowego czasu, aby program działał w tle.
Test 3
Asynchronous, buffered I/O: asynchio /f*.dat Operations completed in the order issued. 500 requests issued and completed in 0.251670 second.
Ten test demonstruje synchroniczną naturę pamięci podręcznej. Wszystkie odczyty zostały wydane i ukończone w 0,251670 sekundy. Innymi słowy żądania asynchroniczne zostały ukończone synchronicznie. Ten test pokazuje również wysoką wydajność menedżera pamięci podręcznej, gdy dane są w pamięci podręcznej.
Test 4
Synchronous, buffered I/O: asynchio /f*.dat /s Operations completed in the order issued. 500 requests and completed in 0.217011 seconds.
Ten test pokazuje te same wyniki co w teście 3. Synchroniczne operacje odczytu z pamięci podręcznej są nieco szybsze niż operacje asynchroniczne odczytu z pamięci podręcznej. Ten test pokazuje również wysoką wydajność menedżera pamięci podręcznej, gdy dane są w pamięci podręcznej.
Podsumowanie
Możesz zdecydować, która metoda jest najlepsza, ponieważ wszystko zależy od typu, rozmiaru i liczby operacji wykonywanych przez program.
Domyślny dostęp do pliku bez określania żadnych flag specjalnych do CreateFile
to operacja synchroniczna i buforowana.
Uwaga 16.
W tym trybie występuje pewne automatyczne zachowanie asynchroniczne, ponieważ sterownik systemu plików wykonuje predykcyjne asynchroniczne operacje odczytu z wyprzedzeniem i asynchroniczne leniwe zapisywanie zmodyfikowanych danych. Chociaż to zachowanie nie sprawia, że asynchroniczne operacje we/wy aplikacji, jest to idealny przypadek dla zdecydowanej większości prostych aplikacji.
Z drugiej strony, jeśli aplikacja nie jest prosta, może być konieczne przeprowadzenie profilowania i monitorowania wydajności w celu określenia najlepszej metody, podobnie jak w testach przedstawionych wcześniej w tym artykule. Profilowanie czasu spędzonego ReadFile
w funkcji or WriteFile
, a następnie porównywanie tego czasu z czasem potrzebnym do ukończenia rzeczywistych operacji we/wy jest przydatne. Jeśli większość czasu jest poświęcana na faktyczne wydawanie operacji we/wy, operacje we/wy są wykonywane synchronicznie. Jeśli jednak czas spędzony na wystawianiu żądań we/wy jest stosunkowo mały w porównaniu z czasem potrzebnym na ukończenie operacji we/wy, operacje są traktowane asynchronicznie. Przykładowy kod wymieniony wcześniej w tym artykule używa QueryPerformanceCounter
funkcji do wykonywania własnych wewnętrznych profilowania.
Monitorowanie wydajności może pomóc w ustaleniu, jak efektywnie program korzysta z dysku i pamięci podręcznej. Śledzenie dowolnego licznika wydajności dla obiektu pamięci podręcznej będzie wskazywać wydajność menedżera pamięci podręcznej. Śledzenie liczników wydajności dla obiektów Dysk fizyczny lub Dysk logiczny będzie wskazywać wydajność systemów dysków.
Istnieje kilka narzędzi, które są przydatne w monitorowaniu wydajności. PerfMon
i DiskPerf
są szczególnie przydatne. Aby system zbierał dane dotyczące wydajności systemów dysków, należy najpierw wydać DiskPerf
polecenie . Po wydaniu polecenia należy ponownie uruchomić system, aby uruchomić zbieranie danych.