Czym dokładnie jest funkcja ponownego wysłania?

198

Większość z tych czasów , definicja reentrance jest cytat z Wikipedii :

Program komputerowy lub procedurę określa się jako ponowne wysłanie, jeśli można bezpiecznie wywołać ją ponownie przed zakończeniem poprzedniego wywołania (tzn. Można bezpiecznie wykonać jednocześnie). Aby ponownie wysłać, program komputerowy lub procedurę:

  1. Nie może zawierać żadnych statycznych (ani globalnych) nie stałych danych.
  2. Nie wolno zwracać adresu statycznym (lub globalnym) niestałym danym.
  3. Musi działać tylko na danych dostarczonych mu przez dzwoniącego.
  4. Nie wolno polegać na blokadach zasobów singletonowych.
  5. Nie wolno modyfikować własnego kodu (chyba że jest wykonywany we własnym unikalnym magazynie wątków)
  6. Nie wolno wywoływać programów komputerowych lub procedur, które nie wymagają ponownej rejestracji.

Jak bezpiecznie zdefiniować?

Jeśli program można bezpiecznie uruchomić jednocześnie , czy zawsze oznacza to, że jest on ponownie wysyłany?

Jaki dokładnie jest wspólny wątek między sześcioma wymienionymi punktami, o którym powinienem pamiętać, sprawdzając kod pod kątem możliwości ponownego wysłania?

Również,

  1. Czy wszystkie funkcje rekurencyjne są ponownie wysyłane?
  2. Czy wszystkie funkcje wątkowo bezpieczne są ponownie wysyłane?
  3. Czy wszystkie rekurencyjne i bezpieczne dla wątków funkcje są ponownie wysyłane?

Pisząc to pytanie, przychodzi mi na myśl jedna kwestia: czy terminy takie jak ponowne wejście i bezpieczeństwo nici są absolutne, tj. Czy mają ustalone konkretne definicje? Jeśli nie są, pytanie to nie ma większego znaczenia.

Lazer
źródło
6
Właściwie nie zgadzam się z numerem 2 na pierwszej liście. Możesz zwrócić adres na cokolwiek zechcesz z funkcji ponownej rejestracji - ograniczenie dotyczy tego, co robisz z tym adresem w kodzie wywoławczym.
2
@Neil Ale skoro autor funkcji ponownego wysyłania nie może kontrolować, z pewnością osoba dzwoniąca z pewnością nie może zwrócić adresu do statycznych (lub globalnych) niestałych danych, aby mogła zostać ponownie wysłana?
Robben_Ford_Fan_boy
2
@drelihan Piszący DOWOLNĄ funkcję (ponownie wysyłającą lub nie) nie jest odpowiedzialny za kontrolowanie działania dzwoniącego za pomocą zwracanej wartości. Z pewnością powinni powiedzieć, co MOŻE z tym zrobić dzwoniący, ale jeśli dzwoniący zdecyduje się zrobić coś innego - pecha.
„bezpieczny dla wątków” jest bez znaczenia, chyba że określisz również, co robią wątki i jaki jest oczekiwany efekt ich działania. Ale może to powinno być osobne pytanie.
Rozumiem bezpiecznie, że zachowanie jest dobrze określone i deterministyczne, niezależnie od harmonogramu.
AturSams,

Odpowiedzi:

191

1. Jak bezpiecznie zdefiniować?

Semantycznie. W tym przypadku nie jest to termin trudny do zdefiniowania. Oznacza to po prostu „Możesz to zrobić bez ryzyka”.

2. Jeśli program może być bezpiecznie wykonywany jednocześnie, czy zawsze oznacza to, że jest ponownie wysyłany?

Nie.

Na przykład, zastosujmy funkcję C ++, która przyjmuje zarówno blokadę, jak i wywołanie zwrotne jako parametr:

#include <mutex>

typedef void (*callback)();
std::mutex m;

void foo(callback f)
{
    m.lock();
    // use the resource protected by the mutex

    if (f) {
        f();
    }

    // use the resource protected by the mutex
    m.unlock();
}

Inna funkcja może wymagać zablokowania tego samego muteksu:

void bar()
{
    foo(nullptr);
}

Na pierwszy rzut oka wszystko wydaje się w porządku… Ale poczekaj:

int main()
{
    foo(bar);
    return 0;
}

Jeśli blokada muteksu nie jest rekurencyjna, oto, co się stanie, w głównym wątku:

  1. mainzadzwoni foo.
  2. foo przejmie zamek.
  3. foozadzwoni bar, co zadzwoni foo.
  4. 2. miejsce foo spróbuje zdobyć zamek, zawiedzie i zaczeka na zwolnienie.
  5. Impas.
  6. Ups…

Ok, oszukiwałem, używając funkcji oddzwaniania. Łatwo jednak wyobrazić sobie bardziej złożone fragmenty kodu o podobnym działaniu.

3. Jaki dokładnie jest wspólny wątek między sześcioma wymienionymi punktami, o którym powinienem pamiętać, sprawdzając kod pod kątem możliwości ponownego wysłania?

Możesz poczuć problem, jeśli twoja funkcja ma / daje dostęp do modyfikowalnego trwałego zasobu lub ma / daje dostęp do funkcji, która wącha .

( Ok, 99% naszego kodu powinno pachnieć, a następnie… Zobacz ostatnią sekcję, aby sobie z tym poradzić… )

Tak więc, studiując kod, jeden z tych punktów powinien cię ostrzec:

  1. Funkcja ma stan (tj. Dostęp do zmiennej globalnej, a nawet zmiennej członka klasy)
  2. Ta funkcja może być wywoływana przez wiele wątków lub może pojawić się dwukrotnie na stosie podczas wykonywania procesu (tj. Funkcja może wywoływać się, bezpośrednio lub pośrednio). Funkcja odbiera oddzwanianie, ponieważ parametry dużo pachną .

Zauważ, że brak ponownego dostępu jest wirusowy: Funkcja, która mogłaby wywołać możliwą funkcję niezwracającą ponownego dostępu, nie może być uznana za ponowne wprowadzenie.

Zauważ też, że metody C ++ pachną, ponieważ mają do nich dostępthis , dlatego powinieneś przestudiować kod, aby upewnić się, że nie występują w nich żadne dziwne interakcje.

4.1 Czy wszystkie funkcje rekurencyjne są ponownie wysyłane?

Nie.

W przypadkach wielowątkowych funkcja rekurencyjna uzyskująca dostęp do współdzielonego zasobu może być wywoływana przez wiele wątków w tym samym momencie, powodując złe / uszkodzone dane.

W przypadkach z pojedynczym wątkiem funkcja rekurencyjna może korzystać z funkcji bez ponownego udostępniania (jak niesławny strtok) lub wykorzystywać dane globalne bez obsługi faktu, że dane są już w użyciu. Zatem twoja funkcja jest rekurencyjna, ponieważ wywołuje się bezpośrednio lub pośrednio, ale wciąż może być niebezpieczna rekurencyjnie .

4.2 Czy wszystkie funkcje wątkowo bezpieczne są ponownie wysyłane?

W powyższym przykładzie pokazałem, że pozornie bezpieczna funkcja nie była ponownie dostępna. OK, oszukiwałem z powodu parametru wywołania zwrotnego. Ale istnieje wiele sposobów zakleszczenia wątku poprzez uzyskanie dwukrotnie blokady nierekurencyjnej.

4.3 Czy wszystkie rekurencyjne i bezpieczne dla wątków funkcje są ponownie wysyłane?

Powiedziałbym „tak”, jeśli przez „rekurencyjny” masz na myśli „bezpieczny rekurencyjny”.

Jeśli możesz zagwarantować, że funkcja może być wywoływana jednocześnie przez wiele wątków i może wywoływać siebie bezpośrednio lub pośrednio, bez problemów, to jest ona ponownie wysyłana.

Problemem jest ocena tej gwarancji… ^ _ ^

5. Czy terminy takie jak ponowne wczytywanie i bezpieczeństwo nici są absolutne, tj. Czy mają ustalone konkretne definicje?

Wierzę, że tak, ale ocena funkcji jest bezpieczna dla wątków lub ponowne wysłanie może być trudne. Dlatego użyłem terminu zapach : możesz znaleźć, że funkcja nie jest ponownie dostępna, ale może być trudno mieć pewność, że złożony fragment kodu jest ponownie dostępny

6. Przykład

Załóżmy, że masz obiekt za pomocą jednej metody, która musi użyć zasobu:

struct MyStruct
{
    P * p;

    void foo()
    {
        if (this->p == nullptr)
        {
            this->p = new P();
        }

        // lots of code, some using this->p

        if (this->p != nullptr)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

Pierwszy problem polega na tym, że jeśli w jakiś sposób funkcja ta zostanie wywołana rekurencyjnie (tj. Ta funkcja wywoła się bezpośrednio lub pośrednio), kod prawdopodobnie ulegnie awarii, ponieważ this->p zostanie usunięty na końcu ostatniego wywołania i prawdopodobnie będzie używany przed końcem pierwszego połączenia.

Dlatego ten kod nie jest bezpieczny dla rekurencji .

Możemy użyć licznika referencyjnego, aby to naprawić:

struct MyStruct
{
    size_t c;
    P * p;

    void foo()
    {
        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        // lots of code, some using this->p
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }
    }
};

W ten sposób kod staje się bezpieczny dla rekurencyjnych… Ale wciąż nie jest ponownie wysyłany z powodu problemów z wielowątkowością: Musimy być pewni, że modyfikacje ci pzostaną wykonane atomowo przy użyciu rekurencyjnego muteksu (nie wszystkie muteksy są rekurencyjne):

#include <mutex>

struct MyStruct
{
    std::recursive_mutex m;
    size_t c;
    P * p;

    void foo()
    {
        m.lock();

        if (c == 0)
        {
            this->p = new P();
        }

        ++c;
        m.unlock();
        // lots of code, some using this->p
        m.lock();
        --c;

        if (c == 0)
        {
            delete this->p;
            this->p = nullptr;
        }

        m.unlock();
    }
};

I oczywiście wszystko to zakłada lots of codesam reentrant, w tym użycie p.

Powyższy kod nie jest nawet zdalnie bezpieczny dla wyjątków , ale to już inna historia… ^ _ ^

7. Hej 99% naszego kodu nie jest ponownie wysyłane!

Jest to całkiem prawdziwe w przypadku kodu spaghetti. Ale jeśli podzielisz poprawnie swój kod, unikniesz problemów z ponownym uruchomieniem.

7.1 Upewnij się, że wszystkie funkcje mają stan NIE

Muszą używać parametrów, własnych zmiennych lokalnych, innych funkcji bez stanu i zwracać kopie danych, jeśli w ogóle zwracają.

7.2 Upewnij się, że Twój obiekt jest „bezpieczny dla rekurencji”

Metoda obiektowa ma dostęp do this, więc dzieli stan ze wszystkimi metodami tej samej instancji obiektu.

Upewnij się więc, że obiekt może być używany w jednym punkcie stosu (tj. Wywołanie metody A), a następnie w innym punkcie (tj. Wywołanie metody B), bez niszczenia całego obiektu. Zaprojektuj swój obiekt, aby upewnić się, że po wyjściu z metody obiekt jest stabilny i poprawny (brak zwisających wskaźników, brak sprzecznych zmiennych składowych itp.).

7.3 Upewnij się, że wszystkie twoje obiekty są poprawnie zamknięte

Nikt inny nie powinien mieć dostępu do swoich danych wewnętrznych:

    // bad
    int & MyObject::getCounter()
    {
        return this->counter;
    }

    // good
    int MyObject::getCounter()
    {
        return this->counter;
    }

    // good, too
    void MyObject::getCounter(int & p_counter)
    {
        p_counter = this->counter;
    }

Nawet zwrócenie stałego odwołania może być niebezpieczne, jeśli użytkownik pobierze adres danych, ponieważ inna część kodu mogłaby go zmodyfikować bez podania kodu stałego odwołania.

7.4 Upewnij się, że użytkownik wie, że Twój obiekt nie jest bezpieczny dla wątków

Dlatego użytkownik jest odpowiedzialny za stosowanie muteksów do używania obiektu współdzielonego między wątkami.

Obiekty z STL są zaprojektowane tak, aby nie były bezpieczne dla wątków (z powodu problemów z wydajnością), a zatem, jeśli użytkownik chce udostępnić std::string dwa wątki, musi chronić swój dostęp za pomocą operacji podstawowych współbieżności;

7.5 Upewnij się, że kod bezpieczny dla wątków jest bezpieczny dla rekurencyjnych

Oznacza to używanie rekurencyjnych muteksów, jeśli uważasz, że ten sam zasób może być użyty dwukrotnie przez ten sam wątek.

paercebal
źródło
1
Krótko mówiąc, tak naprawdę myślę, że w tym przypadku „bezpieczeństwo” jest zdefiniowane - oznacza to, że funkcja będzie działać tylko na podanych zmiennych - tj. Jest skrótem od definicji cytatu poniżej. Chodzi o to, że nie może to oznaczać innych pomysłów bezpieczeństwa.
Przynoszący duszę Joe
Czy przegapiłeś podanie mutexa w pierwszym przykładzie?
detly
@paercebal: twój przykład jest zły. W rzeczywistości nie musisz zawracać sobie głowy wywołaniem zwrotnym, prosta rekurencja miałaby ten sam problem, jeśli taki istnieje, ale jedynym problemem jest to, że zapomniałeś dokładnie powiedzieć, gdzie jest przydzielona blokada.
Yttrill,
3
@Yttrill: Zakładam, że mówisz o pierwszym przykładzie. Użyłem „oddzwaniania”, ponieważ w istocie oddzwonienie pachnie. Oczywiście funkcja rekurencyjna miałaby ten sam problem, ale zwykle można łatwo przeanalizować funkcję i jej rekurencyjną naturę, a zatem wykryć, czy jest to ponowna próba, czy jest w porządku dla rekurencyjności. Z drugiej strony, wywołanie zwrotne oznacza, że ​​autor funkcji wywołującej wywołanie zwrotne nie ma żadnych informacji o tym, co robi wywołanie zwrotne, więc autorowi może być trudno upewnić się, że jego funkcja zostanie ponownie wysłana. To jest trudność, którą chciałem pokazać.
paercebal,
1
@Gab 是 好人: Poprawiłem pierwszy przykład. Dzięki! Program obsługi sygnałów miałby swoje własne problemy, odmienne od ponownego wprowadzenia, ponieważ zwykle, gdy sygnał zostanie podniesiony, tak naprawdę nie można zrobić nic poza zmianą konkretnie zadeklarowanej zmiennej globalnej.
paercebal,
21

„Bezpiecznie” jest zdefiniowane dokładnie tak, jak nakazuje to zdrowy rozsądek - oznacza „robić swoje rzeczy poprawnie bez ingerowania w inne rzeczy”. Sześć przytoczonych przez ciebie punktów jasno określa wymagania, aby to osiągnąć.

Odpowiedzi na 3 pytania to 3 × „nie”.


Czy wszystkie funkcje rekurencyjne są ponownie wysyłane?

NIE!

Dwie równoczesne wywołania funkcji rekurencyjnej mogą łatwo zepsuć się nawzajem, jeśli na przykład uzyskują dostęp do tych samych danych globalnych / statycznych.


Czy wszystkie funkcje wątkowo bezpieczne są ponownie wysyłane?

NIE!

Funkcja jest bezpieczna dla wątków, jeśli nie działa nieprawidłowo, jeśli jest wywoływana jednocześnie. Ale można to osiągnąć np. Za pomocą muteksu, aby zablokować wykonanie drugiego wywołania aż do pierwszego zakończenia, więc tylko jedno wywołanie działa jednocześnie. Ponowne ustanowienie oznacza jednoczesne wykonywanie bez ingerowania w inne wywołania .


Czy wszystkie rekurencyjne i bezpieczne dla wątków funkcje są ponownie wysyłane?

NIE!

Patrz wyżej.

próżniak
źródło
10

Wspólny wątek:

Czy zachowanie jest dobrze zdefiniowane, jeśli procedura jest wywoływana podczas jej przerwania?

Jeśli masz taką funkcję:

int add( int a , int b ) {
  return a + b;
}

Zatem nie jest zależny od żadnego stanu zewnętrznego. Zachowanie jest dobrze określone.

Jeśli masz taką funkcję:

int add_to_global( int a ) {
  return gValue += a;
}

Wynik nie jest dobrze zdefiniowany dla wielu wątków. Informacje mogłyby zostać utracone, gdyby czas był po prostu zły.

Najprostszą formą funkcji ponownego wprowadzania jest coś, co działa wyłącznie na przekazanych argumentach i stałych wartościach. Wszystko inne wymaga specjalnego traktowania lub często nie jest ponownie wysyłane. I oczywiście argumenty nie mogą odnosić się do zmiennych globalnych.

pociągnięty
źródło
7

Teraz muszę rozwinąć mój poprzedni komentarz. Odpowiedź @paercebal jest nieprawidłowa. W przykładowym kodzie nikt nie zauważył, że muteks, który jakoby miał być parametrem, nie został przekazany?

Twierdzę, że nie zgadzam się z wnioskiem: aby funkcja była bezpieczna w obecności współbieżności, musi zostać ponownie uruchomiona. Dlatego współbieżnie bezpieczny (zwykle napisany jako bezpieczny dla wątków) oznacza ponowne wejście na rynek.

Ani wątek bezpieczny, ani ponowny uczestnik nie mają nic do powiedzenia na temat argumentów: mówimy o równoczesnym wykonywaniu funkcji, co nadal może być niebezpieczne, jeśli zostaną użyte nieodpowiednie parametry.

Na przykład memcpy () jest bezpieczny dla wątków i ponownie wchodzi (zwykle). Oczywiście nie będzie działać zgodnie z oczekiwaniami, jeśli zostanie wywołane ze wskaźnikami do tych samych celów z dwóch różnych wątków. Taki jest punkt definicji SGI, nałożenie na klienta obciążenia w celu zapewnienia synchronizacji dostępu do tej samej struktury danych przez klienta.

Ważne jest, aby zrozumieć, że ogólnie jest to nonsens aby operacje wątkowo bezpieczne obejmowały parametry. Jeśli wykonałeś jakiekolwiek programowanie baz danych, zrozumiesz. Pojęcie tego, co jest „atomowe” i może być chronione przez muteks lub inną technikę, jest koniecznie pojęciem użytkownika: przetwarzanie transakcji w bazie danych może wymagać wielu nieprzerwanych modyfikacji. Kto może powiedzieć, które muszą być zsynchronizowane, ale programista klienta?

Chodzi o to, że „zepsucie” nie musi zepsuć pamięci na twoim komputerze przy pomocy niezserializowanych zapisów: uszkodzenie może nadal wystąpić, nawet jeśli wszystkie poszczególne operacje są serializowane. Wynika z tego, że gdy pytasz, czy funkcja jest bezpieczna dla wątków, czy może zostać ponownie uruchomiona, pytanie oznacza dla wszystkich odpowiednio rozdzielonych argumentów: użycie sprzężonych argumentów nie stanowi kontrprzykładu.

Istnieje wiele systemów programistycznych: Ocaml jest jednym, i myślę, że Python również, który ma w sobie wiele kodu, który nie jest ponownie wysyłany, ale który używa globalnej blokady do przeplatania dostępu do wątków. Systemy te nie są ponownie dostępne i nie są bezpieczne pod względem wątków ani współbieżne, działają bezpiecznie po prostu dlatego, że zapobiegają współbieżności na całym świecie.

Dobrym przykładem jest malloc. Nie jest ponownie dostępny i nie jest bezpieczny dla wątków. Jest tak, ponieważ musi on uzyskać dostęp do globalnego zasobu (sterty). Korzystanie z zamków nie zapewnia bezpieczeństwa: zdecydowanie nie jest ponownie dostępne. Gdyby interfejs do malloc był poprawnie zaprojektowany, byłoby możliwe, aby był ponownie dostępny i bezpieczny w wątkach:

malloc(heap*, size_t);

Teraz może być bezpieczny, ponieważ przenosi odpowiedzialność za szeregowanie dostępu współdzielonego do pojedynczej sterty na klienta. W szczególności nie są wymagane żadne prace, jeśli istnieją osobne obiekty sterty. Jeśli używana jest wspólna sterta, klient musi dokonać serializacji dostępu. Użycie blokady wewnątrz funkcji nie wystarczy: wystarczy rozważyć malloc blokujący stertę *, a następnie pojawia się sygnał i wywołuje malloc na tym samym wskaźniku: zakleszczenie: sygnał nie może być kontynuowany, a klient nie może, ponieważ jest przerwany.

Ogólnie rzecz biorąc, zamki nie zapewniają bezpieczeństwa wątków. W rzeczywistości niszczą bezpieczeństwo, niewłaściwie próbując zarządzać zasobem będącym własnością klienta. Blokowanie musi być wykonane przez producenta obiektu, to jedyny kod, który wie, ile obiektów jest tworzonych i jak będą one używane.

Yttrill
źródło
„W związku z tym bezpieczny jednocześnie (zwykle napisany jako bezpieczny dla wątków) oznacza ponowne wejście na rynek”. Jest to sprzeczne z przykładem Wikipedii „Bezpiecznym wątkiem, ale nie ponownym” .
Maggyero
3

„Wspólny wątek” (gra słów zamierzona !?) wśród wymienionych punktów polega na tym, że funkcja nie może robić niczego, co wpłynęłoby na zachowanie jakichkolwiek rekurencyjnych lub współbieżnych wywołań tej samej funkcji.

Na przykład dane statyczne stanowią problem, ponieważ są własnością wszystkich wątków; jeśli jedno wywołanie modyfikuje zmienną statyczną, wszystkie wątki wykorzystują zmodyfikowane dane, co wpływa na ich zachowanie. Problemem może być samodopasowujący się kod (chociaż rzadko się go spotyka, aw niektórych przypadkach można mu zapobiec), ponieważ chociaż istnieje wiele wątków, istnieje tylko jedna kopia kodu; kod jest także niezbędny dla danych statycznych.

Zasadniczo, aby zostać ponownie uruchomionym, każdy wątek musi mieć możliwość korzystania z funkcji tak, jakby był jedynym użytkownikiem, i nie dzieje się tak, jeśli jeden wątek może wpływać na zachowanie drugiego w sposób niedeterministyczny. Przede wszystkim dotyczy to każdego wątku zawierającego osobne lub stałe dane, na których działa funkcja.

Wszystko, co zostało powiedziane, punkt (1) niekoniecznie jest prawdą; na przykład możesz legalnie i zgodnie z projektem użyć zmiennej statycznej, aby zachować liczbę rekurencji, aby zabezpieczyć się przed nadmierną rekurencją lub profilować algorytm.

Funkcja bezpieczna dla wątków nie musi być ponownie wysyłana; może osiągnąć bezpieczeństwo nici, szczególnie zapobiegając ponownemu zaciągnięciu za pomocą zamka, a punkt (6) mówi, że taka funkcja nie jest ponownym przyznaniem. Jeśli chodzi o punkt (6), funkcja, która wywołuje funkcję bezpieczną dla wątków, która blokuje, nie jest bezpieczna do użycia w rekurencji (zablokuje się automatycznie), a zatem nie jest uważana za ponowne wysłanie, chociaż może być jednak bezpieczna dla współbieżności, oraz nadal będzie ponownie dostępny w tym sensie, że wiele wątków może mieć swoje liczniki programów w takiej funkcji jednocześnie (tylko nie z zablokowanym regionem). Może to pomóc w odróżnieniu bezpieczeństwa wątków od ponownego wcielenia (a może zwiększa zamieszanie!).

Clifford
źródło
1

Odpowiedzi na twoje „także” pytania to „nie”, „nie” i „nie”. Tylko dlatego, że funkcja jest rekurencyjna i / lub bezpieczna dla wątków, nie sprawia, że ​​jest ona ponownie dostępna.

Każda z tych funkcji może zawieść we wszystkich punktach, które podajesz. (Chociaż nie jestem w 100% pewien punktu 5).

ChrisF
źródło
1

Terminy „bezpieczny dla wątków” i „uczestnik ponownie” oznaczają tylko i dokładnie to, co mówią ich definicje. „Bezpieczny” w tym kontekście oznacza tylko to, co mówi podana poniżej definicja.

„Bezpieczny” tutaj z pewnością nie oznacza bezpiecznego w szerszym znaczeniu tego słowa, że ​​wywołanie danej funkcji w danym kontekście nie całkowicie zatuszuje twoją aplikację. Podsumowując, funkcja może niezawodnie wywołać pożądany efekt w aplikacji wielowątkowej, ale zgodnie z definicjami nie może być zakwalifikowana jako ponownie wchodząca lub bezpieczna dla wątku. Przeciwnie, możesz wywoływać funkcje ponownej rejestracji na różne sposoby, które przyniosą szereg niepożądanych, nieoczekiwanych i / lub nieprzewidywalnych efektów w aplikacji wielowątkowej.

Funkcja rekurencyjna może być dowolna, a uczestnik ma lepszą definicję niż wątek, więc odpowiedzi na ponumerowane pytania są przeczące.

Czytając definicję ponownej rejestracji, można ją streścić jako funkcję, która nie zmodyfikuje niczego poza tym, co nazywasz modyfikacją. Ale nie powinieneś polegać tylko na podsumowaniu.

Programowanie wielowątkowe jest po prostu niezwykle trudne w ogólnym przypadku. Wiedza, która część kodu ponownie wchodzi na rynek, jest tylko częścią tego wyzwania. Bezpieczeństwo nici nie jest addytywne. Zamiast próbować łączyć funkcje ponownej rejestracji, lepiej jest zastosować ogólny wzorzec projektowy bezpieczny dla wątków i użyć tego wzorca, aby poprowadzić użycie każdego wątku i zasobów współdzielonych w programie.

Joe Soul-Bringer
źródło