Udostępnij przez


Synchroniczne i asynchroniczne operacje we/wy

Zobacz również przykładowe aplikacje związane z we/wy.

Istnieją dwa typy synchronizacji wejścia/wyjścia (We/Wy): synchroniczne We/Wy i asynchroniczne We/Wy. Asynchroniczne operacje we/wy są również określane jako nakładające się operacje we/wy.

W synchronicznym wejścia/wyjścia plików wątek uruchamia operację wejścia/wyjścia i natychmiast wchodzi w stan oczekiwania do momentu ukończenia żądania wejścia/wyjścia. Wątek wykonujący asynchroniczne We/Wy plików wysyła żądanie We/Wy do jądra przez wywołanie odpowiedniej funkcji. Jeśli żądanie zostanie zaakceptowane przez jądro, wątek wywołujący kontynuuje przetwarzanie innego zadania, dopóki jądro nie zasygnalizuje wątkowi, że operacja we/wy została ukończona. Następnie przerywa bieżące zadanie i przetwarza dane z operacji we/wy w razie potrzeby.

Dwa typy synchronizacji przedstawiono na poniższym rysunku.

Zrzut ekranu przedstawiający diagram ilustrujący synchroniczne i asynchroniczne operacje we/wy.

W sytuacjach, gdy żądanie we/wy ma zająć dużo czasu, takie jak odświeżanie lub tworzenie kopii zapasowej dużej bazy danych lub wolne łącze komunikacyjne, asynchroniczne operacje we/wy są ogólnie dobrym sposobem optymalizacji wydajności przetwarzania. Jednak w przypadku stosunkowo szybkich operacji we/wy obciążenie przetwarzania żądań we/wy jądra i sygnałów jądra może sprawić, że asynchroniczne operacje we/wy mogą być mniej korzystne, szczególnie jeśli trzeba wykonać wiele szybkich operacji we/wy. W takim przypadku synchroniczne operacje wejścia/wyjścia byłyby lepsze. Mechanizmy i szczegóły implementacji dotyczące sposobu wykonywania tych zadań różnią się w zależności od typu używanego uchwytu urządzenia i konkretnych potrzeb aplikacji. Innymi słowy, zwykle istnieje wiele sposobów rozwiązania problemu.

Zagadnienia dotyczące synchronicznego i asynchronicznego wejścia/wyjścia

Jeśli plik lub urządzenie jest otwierany do synchronicznego we/wy (tj. FILE_FLAG_OVERLAPPED nie jest określony), kolejne wywołania funkcji takich jak WriteFile mogą blokować wykonywanie wątku wywołującego do momentu wystąpienia jednego z następujących zdarzeń:

  • Operacja we/wy kończy się (w tym przykładzie pisanie danych).
  • Występuje błąd we/wy. (Na przykład rura jest zamknięta z drugiego końca).
  • W samym wywołaniu wystąpił błąd (na przykład co najmniej jeden parametr jest nieprawidłowy).
  • Inny wątek w procesie wywołuje funkcję CancelSynchronousIo używając uchwytu zablokowanego wątku, co przerywa operację we/wy dla tego wątku, powodując niepowodzenie operacji we/wy.
  • Zablokowany wątek jest zakończony przez system; na przykład sam proces jest zakończony lub inny wątek wywołuje funkcję TerminateThread przy użyciu uchwytu zablokowanego wątku. (Zazwyczaj jest to uważane za ostatnią i nie dobrą konstrukcję aplikacji).

W niektórych przypadkach to opóźnienie może być niedopuszczalne dla projektu i celu aplikacji, dlatego projektanci aplikacji powinni rozważyć użycie asynchronicznego we/wy z odpowiednimi obiektami synchronizacji wątków, takimi jak porty uzupełniania we/wy. Aby uzyskać więcej informacji na temat synchronizacji wątków, zobacz Informacje o synchronizacji.

Proces otwiera plik do asynchronicznego we/wy w wywołaniu CreateFile, określając flagę FILE_FLAG_OVERLAPPED w parametrze dwFlagsAndAttributes. Jeśli FILE_FLAG_OVERLAPPED nie zostanie określony, plik zostanie otwarty dla synchronicznych operacji we/wy. Po otwarciu pliku dla asynchronicznego I/O wskaźnik do struktury OVERLAPPED jest przekazywany do wywołania ReadFile i WriteFile. Podczas wykonywania synchronicznych operacji we/wy ta struktura nie jest wymagana w wywołaniach plików ReadFile i WriteFile.

Uwaga / Notatka

Jeśli plik lub urządzenie jest otwierane dla asynchronicznego we/wy, kolejne wywołania funkcji, takich jak WriteFile przy użyciu tego uchwytu, są zwykle zwracane natychmiast, ale mogą również zachowywać się synchronicznie w odniesieniu do zablokowanego wykonywania. Aby uzyskać więcej informacji, zobacz Asynchroniczne operacje we/wy dysku są wyświetlane jako synchroniczne w systemie Windows.

Chociaż funkcja CreateFile jest najczęściej używana do otwierania plików, woluminów dysków, potoków anonimowych i innych podobnych urządzeń, operacje I/O mogą być również wykonywane przy użyciu uchwytu z rzutowaniem typu z innych obiektów systemowych, takich jak socket utworzonego przez funkcję accept.

Dojścia do obiektów katalogu są uzyskiwane przez wywołanie funkcji CreateFile za pomocą atrybutu FILE_FLAG_BACKUP_SEMANTICS . Dojścia katalogów są prawie nigdy nieużytowane — aplikacje do tworzenia kopii zapasowych są jedną z niewielu aplikacji, które zwykle ich używają.

Po otwarciu obiektu pliku dla asynchronicznego we/wy, struktura OVERLAPPED musi być prawidłowo utworzona, zainicjowana i przekazana do każdego wywołania funkcji, takich jak ReadFile i WriteFile. Podczas korzystania ze struktury OVERLAPPED w operacjach asynchronicznych odczytu i zapisu należy pamiętać o następujących kwestiach:

  • Nie cofnij przydziału ani nie modyfikuj struktury OVERLAPPED ani buforu danych, dopóki nie zostaną ukończone wszystkie operacje asynchroniczne we/wy do obiektu pliku.
  • Jeśli zadeklarujesz swój wskaźnik do struktury OVERLAPPED jako zmienną lokalną, nie opuszczaj funkcji lokalnej, dopóki nie zostaną ukończone wszystkie asynchroniczne operacje I/O dla obiektu pliku. Jeśli funkcja lokalna zostanie przedwcześnie zakończona, struktura OVERLAPPED wyjdzie poza zakres i będzie niedostępna dla wszystkich funkcji ReadFile lub WriteFile , które napotka poza tę funkcję.

Można również utworzyć zdarzenie i umieścić uchwyt w strukturze OVERLAPPED; funkcje oczekiwania mogą być następnie użyte do oczekiwania na zakończenie operacji we/wy, wykorzystując uchwyt zdarzenia.

Jak wspomniano wcześniej, podczas pracy z asynchronicznym uchwytem aplikacje powinny zachować ostrożność, określając, w jakim momencie zwolnić zasoby związane z określoną operacją we/wy na tym uchwycie. Jeśli uchwyt zostanie zdealokowany przedwcześnie, ReadFile lub WriteFile może błędnie zgłosić, że operacja We/Wy została ukończona. Ponadto funkcja WriteFile czasami zwraca TRUE przy wartości GetLastError równej ERROR_SUCCESS, mimo że używa asynchronicznego uchwytu operacji (co może również zwracać FALSE z ERROR_IO_PENDING). Programiści przyzwyczajeni do synchronicznego projektowania we/wy zwykle zwalniają w tym momencie zasoby buforu danych, ponieważ TRUE i ERROR_SUCCESS oznaczają, że operacja jest zakończona. Jednak jeśli porty ukończenia we/wy są używane z tym asynchronicznym uchwytem, pakiet ukończenia zostanie wysłany, mimo że operacja we/wy została ukończona natychmiast. Innymi słowy, jeśli aplikacja zwalnia zasoby po tym, jak WriteFile zwraca TRUE z ERROR_SUCCESS, a także w procedurze portu zakończenia we/wy, będzie występować warunek błędu podwójnego zwolnienia. W tym przykładzie zaleceniem byłoby, aby procedura obsługi portu ukończenia była wyłącznie odpowiedzialna za wszystkie operacje zwalniania takich zasobów.

System nie obsługuje wskaźnika plików w asynchronicznych dojściach do plików i urządzeń, które wspierają wskaźniki plików, dlatego pozycja pliku musi zostać przekazana do funkcji odczytu i zapisu w powiązanych członkach danych o przesunięciu struktury OVERLAPPED. Aby uzyskać więcej informacji, zobacz WriteFile i ReadFile.

Położenie wskaźnika pliku dla synchronizowanego uchwytu jest utrzymywane przez system podczas odczytu lub zapisu danych i może być również aktualizowane przy użyciu funkcji SetFilePointer lub SetFilePointerEx.

Aplikacja może również poczekać na uchwyt pliku, aby zsynchronizować zakończenie operacji we/wy, ale wymaga to skrajnej ostrożności. Za każdym razem, gdy operacja we/wy jest uruchamiana, system operacyjny ustawia uchwyt pliku na stan niezasygnalizowany. Za każdym razem, gdy operacja we/wy zostanie ukończona, system operacyjny ustawia uchwyt pliku na stan sygnalizowany. W związku z tym, jeśli aplikacja uruchamia dwie operacje I/O i czeka na uchwycie, nie ma możliwości określenia, która operacja została zakończona po ustawieniu uchwytu w stan zasygnalizowany. Jeśli aplikacja musi wykonywać wiele asynchronicznych operacji we/wy w jednym pliku, powinna oczekiwać na uchwyt zdarzenia w określonej strukturze OVERLAPPED dla każdej operacji we/wy, a nie na wspólny uchwyt pliku.

Anulowanie operacji wejścia/wyjścia

Aby anulować wszystkie oczekujące operacje we/wy asynchroniczne, użyj jednej z następujących metod:

  • CancelIo: Ta funkcja anuluje tylko operacje zainicjowane przez wątek wywołujący dla określonego uchwytu pliku.
  • CancelIoEx: ta funkcja anuluje wszystkie operacje rozpoczęte przez wątki dla określonego uchwytu pliku.

Użyj CancelSynchronousIo, aby anulować oczekujące synchroniczne operacje we/wy.

Funkcje ReadFileEx i WriteFileEx umożliwiają aplikacji określenie procedury do wykonania (zobacz FileIOCompletionRoutine) po zakończeniu asynchronicznego żądania we/wy.

WriteFile (ZapiszPlik)

Odczyt pliku