Próbuję utworzyć usługę, która zawiera wiele gniazd klientów i serwerów (usługa serwera, a także klientów, które łączą się z zarządzanymi komponentami i są przechowywane), które są synchronicznie odpytywane przez IO::Select
. Pomysł polegał na obsłużeniu operacji we/wy i/lub żądania przetwarzania, które powstają w wyniku puli wątków roboczych.Czy niezablokowane operacje we/wy w Perlu są ograniczone do jednego wątku? Dobry projekt?
Słowo kluczowe, które umożliwia współdzielenie danych przez wątki w Perlu (threads::shared
) ma swoje ograniczenia - odniesienia do uchwytów nie należą do prymitywów, które można udostępnić.
Zanim zorientowali się, że uchwyty i/lub uchwyt referencje nie mogą być udostępniane, w planie było mieć select()
wątek, który dba o odpytywania, a następnie umieszcza odpowiednie uchwyty w pewnym ThreadQueue
y rozłożone puli gwintu właściwie robię czytanie i pisanie. (Oczywiście projektowałem to tak, aby modyfikacja rzeczywistych zestawów deskryptorów używanych przez select
była bezpieczna dla wątków i odbywała się tylko w jednym wątku - tym samym, który działa select()
, a więc nigdy podczas jego działania, oczywiście.)
To nie wygląda na to, że stanie się to teraz, ponieważ same uchwyty nie mogą być udostępniane, więc zarówno głosowanie, jak i czytanie i pisanie będą musiały się zdarzyć z jednego wątku. Czy istnieje jakieś obejście tego problemu? Mam na myśli rozkład rzeczywistych wywołań systemowych przez wątki; są oczywiście sposoby na wykorzystanie kolejek i buforów, aby uzyskać dane wytworzone w innych wątkach i faktycznie wysłane w innych.
Jednym z problemów, które wynikają z tej sytuacji jest to, że muszę dać select()
limit czasu i oczekiwać, że będzie wystarczająco wysoka, aby nie powodować problemów z odpytywaniem dość dużego zestawu deskryptorów, a jednocześnie wystarczająco niska, aby nie wprowadzić dużo opóźnień w pętli zdarzeń czasowych - chociaż rozumiem, że jeśli w procesie odpytywania wykryte zostanie rzeczywiste członkostwo we/wy, select()
wróci wcześniej, co częściowo złagodzi problem. Wolałbym mieć jakiś sposób na obudzenie się z innego wątku, ale ponieważ uchwytów nie można udostępnić, nie mogę łatwo wymyślić sposobu na zrobienie tego, ani zobaczyć korzyści z tego; O czym jest inny wątek, o którym warto wiedzieć, kiedy należy obudzić się na select()
?
Jeśli nie można obejść tego problemu, jaki jest dobry wzór projektu dla tego typu usługi w języku Perl? Mam wymaganie dość dużej skalowalności i współbieżnych operacji wejścia/wyjścia, i z tego powodu przeszedłem ścieżkę bez blokowania, a nie tylko odradzam wątki dla każdego gniazda nasłuchu i/lub procesu klienta i/lub serwera, jak wielu ludzi używających języki pozycyjne w dzisiejszych czasach będą działać w przypadku gniazd - wydaje się, że jest to typowa praktyka w krainie Jawy i nikt nie wydaje się być zainteresowany poza wąskim obszarem programowania zorientowanego na systemy. Może to tylko moje wrażenie. W każdym razie nie chcę tego robić w ten sposób.
A więc, z punktu widzenia doświadczonego programisty systemów Perla, w jaki sposób powinno się to zorganizować? Monolityczny wątek we/wy + wątki czystego pracownika (inne niż I/O) + wiele kolejek? Jakiś sprytny hack? Jakieś wątki bezpieczeństwa, które wychodzą poza to, co już wyliczyłem? Czy istnieje lepsza droga? Mam duże doświadczenie w projektowaniu tego typu programów w języku C, ale nie przy użyciu idiomów języka Perl i cech środowiska wykonawczego.
EDYCJA: P.S. Zdecydowanie przyszło mi do głowy, że być może program z tymi wymaganiami wydajnościowymi i tym projektem powinien po prostu nie być napisany w Perlu. Ale widzę strasznie dużo bardzo wyrafinowanych usług produkowanych w Perlu, więc nie jestem tego pewien.
Interesujące sugestie. Szczególnie podoba mi się podejście polegające na przejściu leżącego poniżej FD, a następnie zbudowaniu z niego uchwytu później. Czy możesz zaproponować sposób inicjalizacji uchwytu pliku, a następnie przypisać go do IO :: Socket później, w wątku? Wolałbym nie tworzyć gniazd w jednym wątku, a następnie manipulować nimi w innym; czy można zrobić coś typowego, jak IO :: Handle-> new? –
Alex, bez pseudokodu, nie jestem pewien, czy rozumiem szczegóły twojego pytania tutaj. Jednak użycie "IO :: Socket :: INET" i "IO :: Socket :: INET-> new_from_fd ($ my_duplicated_fd," r + ")" spowoduje pobranie obiektu IO :: Socket. – pilcrow
Och, nie zdawałem sobie sprawy, że IO :: Socket :: INET jest podklasą IO :: Handle. Ma sens. –