Piszę wielowątkowy serwer zgodny z POSIX w c/C++, który musi być w stanie akceptować, czytać i pisać do dużej liczby połączenia asynchronicznie. Serwer ma kilka wątków roboczych, które wykonują zadania i sporadycznie (i nieprzewidywalnie) dane kolejki do zapisania w gniazdach. Dane są również sporadycznie (i nieprzewidywalnie) zapisywane na gniazdach przez klientów, więc serwer musi również czytać asynchronicznie. Jednym z oczywistych sposobów jest przekazanie każdemu połączeniu wątku, który czyta i zapisuje z/do gniazda; jest to jednak brzydkie, ponieważ każde połączenie może utrzymywać się przez długi czas, a serwer może w związku z tym posiadać setki tysięcy wątków, aby śledzić połączenia.Oczekiwanie na warunek (pthread_cond_wait) i zmianę gniazda (wybierz) jednocześnie
Lepszym podejściem byłoby posiadanie pojedynczego wątku, który obsługuje całą komunikację za pomocą funkcji select()/pselect(). Oznacza to, że pojedynczy wątek czeka na każdym gnieździe, aby był czytelny, a następnie spawnuje zadanie do przetworzenia danych wejściowych, które będą obsługiwane przez pulę innych wątków za każdym razem, gdy dane wejściowe będą dostępne. Za każdym razem, gdy inne wątki robocze generują dane wyjściowe dla połączenia, zostaje ono umieszczone w kolejce, a wątek komunikacyjny czeka, aż to gniazdo będzie zapisywalne przed jego zapisaniem.
Problem polega na tym, że wątek komunikacyjny może czekać w funkcji select() lub pselect(), gdy dane wyjściowe są umieszczane w kolejce przez wątki robocze serwera. Możliwe, że jeśli żaden sygnał wejściowy nie nadejdzie przez kilka sekund lub minut, kolejka fragmentu wyjścia będzie czekać na zakończenie wątku komunikacyjnego select() ing. Nie powinno się to jednak zdarzyć - dane powinny zostać zapisane tak szybko, jak to możliwe.
W tej chwili widzę kilka rozwiązań, które są bezpieczne dla wątków. Jedną z nich jest zajęcie wątku komunikacyjnego - oczekiwanie na dane wejściowe i aktualizacja listy gniazd, które czekają na pisanie co dziesiątą część sekundy. Nie jest to optymalne, ponieważ wymaga dużego oczekiwania, ale zadziała. Inną opcją jest użycie pselect() i wysłanie sygnału USR1 (lub czegoś podobnego) za każdym razem, gdy nowe dane wyjściowe zostały umieszczone w kolejce, dzięki czemu wątek komunikacyjny może zaktualizować listę gniazd, na które czeka od razu status zapisu. Wolę to ostatnie, ale nadal nie lubię używania sygnału do czegoś, co powinno być warunkiem (pthread_cond_t). Jeszcze jedną opcją jest włączenie na liście deskryptorów plików, na których czeka (select) oczekiwany plik obojętny, w którym zapisujemy jeden bajt, aby zawsze trzeba było dodać gniazdo do zapisywalnego zestawu fd_set dla select(); to obudziłoby serwer komunikacyjny, ponieważ ten konkretny plik byłby czytelny, dzięki czemu wątek komunikacyjny mógł natychmiast zaktualizować jego zapisywalny zestaw fd_set.
Czuję intuicyjnie, że drugie podejście (z sygnałem) jest "najbardziej poprawnym" sposobem programowania serwera, ale jestem ciekawy, czy ktoś wie, który z powyższych jest najbardziej wydajny, ogólnie rzecz biorąc, czy którykolwiek z powyższych spowoduje warunki wyścigu, których nie znam, lub jeśli ktokolwiek wie o bardziej ogólnym rozwiązaniu tego problemu. To, czego naprawdę chcę, to funkcja pthread_cond_wait_and_select(), która pozwala wątkowi komunikacyjnemu oczekiwać zarówno na zmianę gniazd, jak i sygnał ze stanu.
Z góry dziękuję.