Udostępnij za pośrednictwem


Porty uzupełniania we/wy

Porty uzupełniania we/wy zapewniają wydajny model wątkowania do przetwarzania wielu asynchronicznych żądań we/wy w systemie wieloprocesorowym. Gdy proces tworzy port uzupełniania we/wy, system tworzy skojarzony obiekt kolejki dla wątków, których jedynym celem jest obsługa tych żądań. Procesy obsługujące wiele współbieżnych żądań we/wy asynchronicznych mogą wykonywać to szybciej i wydajniej przy użyciu portów uzupełniania we/wy w połączeniu ze wstępnie przydzieloną pulą wątków niż przez utworzenie wątków w momencie otrzymania żądania we/wy.

Jak działają porty uzupełniania we/wy

Funkcja CreateIoCompletionPort tworzy port uzupełniania we/wy i kojarzy jeden lub więcej dojść do pliku z tym portem. Gdy operacja we/wy asynchronicznej na jednym z tych dojść do plików zostanie ukończona, pakiet uzupełniania we/wy jest w kolejce w kolejności wejścia/wyjścia (FIFO) do skojarzonego portu uzupełniania we/wy. Jednym z zaawansowanych zastosowań tego mechanizmu jest połączenie punktu synchronizacji dla wielu dojść plików w jeden obiekt, chociaż istnieją również inne przydatne aplikacje. Należy pamiętać, że podczas gdy pakiety są kolejkowane w kolejności FIFO, mogą być w kolejce w innej kolejności.

Nuta

Termin dojście do pliku, jak użyto tutaj, odnosi się do abstrakcji systemu reprezentującej nakładający się punkt końcowy we/wy, a nie tylko plik na dysku. Może to być na przykład punkt końcowy sieci, gniazdo TCP, nazwany potok lub gniazdo poczty. Można użyć dowolnego obiektu systemowego obsługującego nakładające się we/wy. Aby uzyskać listę powiązanych funkcji we/wy, zobacz koniec tego tematu.

 

Gdy uchwyt pliku jest skojarzony z portem ukończenia, blok stanu przekazany w pliku nie zostanie zaktualizowany, dopóki pakiet nie zostanie usunięty z portu ukończenia. Jedynym wyjątkiem jest to, że oryginalna operacja zwraca synchronicznie z błędem. Wątek (utworzony przez główny wątek lub główny wątek) używa funkcji GetQueuedCompletionStatus czekać na ukończenie pakietu ukończenia do portu ukończenia we/wy, a nie czekać bezpośrednio na zakończenie asynchronicznego we/wy. Wątki, które blokują wykonywanie na porcie uzupełniania we/wy, są zwalniane w kolejności ostatniego wyjścia (LIFO), a następny pakiet uzupełniania jest ściągany z kolejki FIFO portu uzupełniania we/wy dla tego wątku. Oznacza to, że po wydaniu pakietu ukończenia do wątku system zwalnia ostatni (najnowszy) wątek skojarzony z tym portem, przekazując mu informacje o uzupełnianiu dla najstarszego ukończenia operacji we/wy.

Mimo że dowolna liczba wątków może wywołać GetQueuedCompletionStatus dla określonego portu uzupełniania we/wy, gdy określony wątek wywołuje GetQueuedCompletionStatus po raz pierwszy, staje się skojarzony z określonym portem uzupełniania we/wy do momentu wystąpienia jednej z trzech rzeczy: wątek kończy działanie, określa inny port uzupełniania we/wy, lub zamyka port uzupełniania we/wy. Innymi słowy, pojedynczy wątek może być skojarzony z co najwyżej jednym portem uzupełniania we/wy.

Gdy pakiet ukończenia jest kolejkowany do portu uzupełniania we/wy, system najpierw sprawdza, ile wątków skojarzonych z tym portem jest uruchomionych. Jeśli liczba uruchomionych wątków jest mniejsza niż wartość współbieżności (omówiona w następnej sekcji), jeden z wątków oczekujących (najnowszych) może przetworzyć pakiet ukończenia. Gdy uruchomiony wątek zakończy przetwarzanie, zwykle wywołuje GetQueuedCompletionStatus ponownie, w którym momencie zwracany jest następny pakiet ukończenia lub czeka, jeśli kolejka jest pusta.

Wątki mogą używać funkcji PostQueuedCompletionStatus do umieszczania pakietów uzupełniania w kolejce portu uzupełniania we/wy. Dzięki temu port ukończenia może służyć do odbierania komunikacji z innych wątków procesu, oprócz odbierania pakietów uzupełniania we/wy z systemu we/wy. Funkcja PostQueuedCompletionStatus umożliwia aplikacji kolejkę własnych pakietów uzupełniania specjalnego przeznaczenia do portu uzupełniania we/wy bez uruchamiania asynchronicznej operacji we/wy. Jest to przydatne w przypadku powiadamiania wątków procesów roboczych o zdarzeniach zewnętrznych, na przykład.

Dojście do portu uzupełniania we/wy i każdy uchwyt pliku skojarzony z tym konkretnym portem uzupełniania we/wy są znane jako odwołania do portu uzupełniania we/wy. Port ukończenia we/wy jest zwalniany, gdy nie ma więcej odwołań do niego. W związku z tym wszystkie te dojścia muszą być prawidłowo zamknięte, aby zwolnić port uzupełniania we/wy i skojarzone z nim zasoby systemowe. Po spełnieniu tych warunków aplikacja powinna zamknąć dojście portów uzupełniania we/wy, wywołując funkcję CloseHandle.

Nuta

Port ukończenia we/wy jest skojarzony z procesem, który go utworzył i nie jest możliwy do udostępniania między procesami. Jednak pojedyncze dojście jest dostępne między wątkami w tym samym procesie.

 

Wątki i współbieżność

Najważniejszą właściwością portu uzupełniania we/wy do rozważenia jest wartość współbieżności. Wartość współbieżności portu ukończenia jest określana podczas jego tworzenia za pomocą parametru CreateIoCompletionPort za pośrednictwem parametru NumberOfConcurrentThreads. Ta wartość ogranicza liczbę wątków możliwych do uruchomienia skojarzonych z portem uzupełniania. Gdy łączna liczba wątków możliwych do uruchomienia skojarzonych z portem ukończenia osiągnie wartość współbieżności, system blokuje wykonywanie wszystkich kolejnych wątków skojarzonych z tym portem ukończenia, dopóki liczba wątków możliwych do uruchomienia spadnie poniżej wartości współbieżności.

Najbardziej wydajny scenariusz występuje, gdy w kolejce oczekują pakiety ukończenia, ale nie można oczekiwać, ponieważ port osiągnął limit współbieżności. Zastanów się, co dzieje się z wartością współbieżności jednego i wielu wątków oczekujących w GetQueuedCompletionStatus wywołania funkcji. W takim przypadku, jeśli kolejka zawsze ma pakiety ukończenia oczekujące, gdy uruchomiony wątek wywołuje GetQueuedCompletionStatus, nie będzie blokować wykonywania, ponieważ, jak wspomniano wcześniej, kolejka wątków jest LIFO. Zamiast tego ten wątek natychmiast odbierze następny pakiet ukończenia w kolejce. Nie zostaną wykonane żadne przełączniki kontekstowe wątku, ponieważ uruchomiony wątek stale pobiera pakiety ukończenia, a inne wątki nie są w stanie uruchomić.

Nuta

W poprzednim przykładzie dodatkowe wątki wydają się być bezużyteczne i nigdy nie są uruchamiane, ale zakłada, że uruchomiony wątek nigdy nie zostanie umieszczony w stanie oczekiwania przez jakiś inny mechanizm, kończy się lub w inny sposób zamyka skojarzony port uzupełniania we/wy. Podczas projektowania aplikacji należy wziąć pod uwagę wszystkie takie konsekwencje wykonywania wątków.

 

Najlepszą ogólną maksymalną wartością wybraną dla wartości współbieżności jest liczba procesorów CPU na komputerze. Jeśli transakcja wymaga długiego obliczenia, większa wartość współbieżności umożliwi uruchomienie większej liczby wątków. Ukończenie każdego pakietu ukończenia może potrwać dłużej, ale w tym samym czasie będą przetwarzane więcej pakietów ukończenia. Możesz eksperymentować z wartością współbieżności w połączeniu z narzędziami profilowania, aby uzyskać najlepszy efekt dla aplikacji.

System umożliwia również wątek oczekiwania w GetQueuedCompletionStatus przetwarzania pakietu ukończenia, jeśli inny wątek uruchomiony skojarzony z tym samym portem uzupełniania we/wy wprowadza stan oczekiwania z innych powodów, na przykład funkcja SuspendThread. Gdy wątek w stanie oczekiwania zacznie działać ponownie, może wystąpić krótki okres, gdy liczba aktywnych wątków przekracza wartość współbieżności. Jednak system szybko zmniejsza tę liczbę, nie zezwalając na żadne nowe aktywne wątki, dopóki liczba aktywnych wątków nie spadnie poniżej wartości współbieżności. Jest to jeden z powodów, dla których aplikacja tworzy więcej wątków w puli wątków niż wartość współbieżności. Zarządzanie pulą wątków wykracza poza zakres tego tematu, ale dobrą regułą jest posiadanie co najmniej dwukrotnie większej liczby wątków w puli wątków, ponieważ istnieją procesory w systemie. Aby uzyskać dodatkowe informacje na temat buforowania wątków, zobacz Thread Pools.

Obsługiwane funkcje we/wy

Poniższe funkcje mogą służyć do uruchamiania operacji we/wy, które zakończą się przy użyciu portów uzupełniania we/wy. Aby włączyć mechanizm uzupełniania we/wy, należy przekazać funkcję wystąpienia struktury OVERLAPPED i uchwyt pliku skojarzony wcześniej z portem uzupełniania we/wy (przez wywołanie polecenia CreateIoCompletionPort) :

Informacje o procesach i wątkach

BindIoCompletionCallback

CreateIoCompletionPort

GetQueuedCompletionStatus

GetQueuedCompletionStatusEx

PostQueuedCompletionStatus