Zwolennicy FP twierdzili, że współbieżność jest łatwa, ponieważ ich paradygmat unika stanu zmienności. Nie rozumiem
Chciałem poruszyć to ogólne pytanie jako ktoś, kto jest funkcjonalnym neofitą, ale przez lata działał mi na oczy w zakresie skutków ubocznych i chciałbym je złagodzić z różnych powodów, w tym łatwiejszych (a konkretnie „bezpieczniejszych, mniej podatna na błędy ”). Kiedy spoglądam na moich funkcjonalnych rówieśników i to, co robią, trawa wydaje się nieco bardziej zielona i ładniej pachnie, przynajmniej pod tym względem.
Algorytmy szeregowe
To powiedziawszy, o twoim konkretnym przykładzie, jeśli twój problem ma charakter szeregowy i B nie może zostać wykonany, dopóki A nie zostanie zakończone, koncepcyjnie nie możesz uruchomić A i B równolegle, bez względu na wszystko. Musisz znaleźć sposób na przełamanie zależności między kolejnością, jak w odpowiedzi na podstawie wykonywania równoległych ruchów przy użyciu starego stanu gry, lub użyj struktury danych, która pozwala na jej niezależną modyfikację, aby wyeliminować zależność od kolejności, jak zaproponowano w innych odpowiedziach lub coś w tym rodzaju. Ale z pewnością istnieje wiele problemów z projektowaniem koncepcyjnym, takich jak ten, w których niekoniecznie wystarczy tak łatwo napisać wszystko, ponieważ wszystko jest niezmienne. Niektóre rzeczy będą miały charakter szeregowy, dopóki nie znajdziesz sprytnego sposobu na przerwanie zależności od zamówienia, jeśli to w ogóle możliwe.
Łatwiejsza współbieżność
To powiedziawszy, istnieje wiele przypadków, w których nie udaje się ujednolicić programów, które wiążą się z efektami ubocznymi w miejscach, które mogłyby potencjalnie znacznie poprawić wydajność po prostu ze względu na możliwość, że może nie być wątkowo bezpieczny. Jednym z przypadków, w których wyeliminowanie stanu zmiennego (a ściślej zewnętrznych efektów ubocznych) jest bardzo pomocne, jak widzę, jest to, że zmienia on „może być bezpieczny dla wątku” w „zdecydowanie bezpieczny dla wątku” .
Aby uczynić to zdanie nieco bardziej konkretnym, rozważmy, że daję ci zadanie zaimplementowania funkcji sortowania w C, która akceptuje komparator i używa go do sortowania tablicy elementów. Ma to być dość ogólne, ale dam ci proste założenie, że będzie on używany w porównaniu z danymi wejściowymi o takiej skali (miliony elementów lub więcej), że bez wątpienia korzystne będzie zawsze stosowanie wielowątkowej implementacji. Czy potrafisz wielowątkowo korzystać z funkcji sortowania?
Problem polega na tym, że nie możesz, ponieważ komparatory mogą wywoływać funkcje sortowaniapowodować skutki uboczne, chyba że wiesz, jak są one implementowane (lub przynajmniej udokumentowane) dla wszystkich możliwych przypadków, co jest w pewnym stopniu niemożliwe bez zdegenerowania funkcji. Komparator mógłby zrobić coś obrzydliwego, na przykład zmodyfikować zmienną globalną wewnątrz w sposób nieatomowy. 99,9999% komparatorów może tego nie zrobić, ale nadal nie możemy wielowątkowo uogólnić tej funkcji tylko z powodu 0,00001% przypadków, które mogą powodować działania niepożądane. W rezultacie być może będziesz musiał zaoferować zarówno funkcję sortowania jednowątkowego, jak i wielowątkowego oraz przekazać odpowiedzialność programistom, którzy będą z niej korzystać, aby zdecydować, którego z nich użyć na podstawie bezpieczeństwa wątków. Ludzie mogą nadal korzystać z wersji jednowątkowej i tracić możliwość wielowątkowości, ponieważ mogą być również niepewni, czy komparator jest bezpieczny dla wątków,
Istnieje ogromna siła mózgu, która może być zaangażowana w racjonalizację bezpieczeństwa nici bez rzucania wszędzie zamków, które mogą odejść, jeśli tylko mamy twarde gwarancje, że funkcje nie spowodują skutków ubocznych na teraz i na przyszłość. I jest strach: praktyczny strach, ponieważ każdy, kto zbyt wiele razy musiał debugować warunki wyścigu, prawdopodobnie wahałby się przed wielowątkowością wszystkiego, czego nie ma 110% pewności, że jest bezpieczny dla wątków i taki pozostanie. Nawet dla najbardziej paranoicznych (z których prawdopodobnie jestem przynajmniej na granicy) czysta funkcja zapewnia poczucie ulgi i pewności, którą możemy bezpiecznie nazwać równolegle.
I to jest jeden z głównych przypadków, w których uważam to za tak korzystne, jeśli można uzyskać twardą gwarancję, że takie funkcje są bezpieczne dla wątków, które można uzyskać przy użyciu czysto funkcjonalnych języków. Po drugie, języki funkcjonalne często promują tworzenie funkcji wolnych od efektów ubocznych. Mogą na przykład zapewnić trwałe struktury danych, w których wprowadzenie dość masywnej struktury danych jest dość wydajne, a następnie wydrukowanie zupełnie nowej, z niewielką jej częścią zmienioną w stosunku do oryginału bez dotykania oryginału. Osoby pracujące bez takich struktur danych mogą chcieć modyfikować je bezpośrednio i tracić po drodze bezpieczeństwo wątków.
Skutki uboczne
To powiedziawszy, nie zgadzam się z jedną częścią z całym szacunkiem dla moich funkcjonalnych przyjaciół (których uważam za super fajnych):
[...] ponieważ ich paradygmat unika stanu zmienności.
To niekoniecznie niezmienność sprawia, że współbieżność jest tak praktyczna, jak ją widzę. To funkcje, które unikają powodowania skutków ubocznych. Jeśli funkcja wprowadza tablicę do sortowania, kopiuje ją, a następnie mutuje kopię w celu posortowania jej zawartości i wysyła kopię, jest ona tak samo bezpieczna dla wątków jak ta, która działa z niezmiennym typem tablicy, nawet jeśli przekazujesz to samo wejście tablica do niego z wielu wątków. Sądzę więc, że nadal istnieje miejsce na zmienne typy w tworzeniu kodu bardzo przyjaznego dla współbieżności, że tak powiem, chociaż istnieje wiele dodatkowych korzyści dla niezmiennych typów, w tym trwałych struktur danych, których używam nie tyle ze względu na ich niezmienne właściwości, ale wyeliminuj koszty głębokiego kopiowania wszystkiego, aby stworzyć funkcje wolne od skutków ubocznych.
Często wiąże się to z koniecznością narzucenia funkcji wolnych od efektów ubocznych, takich jak tasowanie i kopiowanie dodatkowych danych, być może dodatkowego poziomu pośredniego i ewentualnie GC na części trwałej struktury danych, ale patrzę na jednego z moich znajomych, który ma 32-rdzeniowa maszyna i myślę, że wymiana jest prawdopodobnie tego warta, jeśli możemy bardziej pewnie robić więcej rzeczy równolegle.