Ponowne wprowadzanie kodu a Bezpieczeństwo wątku

Jaka jest różnica między pojęciami "RE-entrancy Code" i "Thread Safety"? Zgodnie z poniższym linkiem fragment kodu może być jednym z nich, obydwoma lub żadnym z nich.

Reentrant and Thread safe code

Nie byłem w stanie zrozumieć wyjaśnienia jasno. Pomoc będzie mile widziana.

Author: Codex, 2008-12-09

2 answers

Kod uczestnika nie ma stanu w jednym punkcie. Możesz wywołać kod, gdy coś jest wykonywane w kodzie. Jeśli kod używa stanu globalnego, jedno wywołanie może nadpisać stan globalny, przerywając obliczenia w drugim wywołaniu.

Thread safe code to kod bez warunków rasowych lub innych problemów z współbieżnością. Warunek race jest, gdy kolejność, w której dwa wątki zrobić coś wpływa na obliczenia. Typowy problem współbieżności polega na zmianie współdzielonych danych struktura może być częściowo zakończona i pozostawiona w stanie niespójnym. Aby tego uniknąć, musisz użyć mechanizmów kontroli współbieżności, takich jak semafory muteksów, aby upewnić się, że nic innego nie może uzyskać dostępu do struktury danych, dopóki operacja nie zostanie zakończona.

Na przykład, fragment kodu może być nieodwracalny, ale bezpieczny dla wątku, jeśli jest strzeżony zewnętrznie przez mutex, ale nadal ma globalną strukturę danych, w której stan musi być spójny przez cały czas trwania wywołania. W w tym przypadku ten sam wątek mógłby zainicjować odwołanie do procedury, a jednocześnie był chroniony zewnętrznym gruboziarnistym mutexem. Jeśli wywołanie miało miejsce w ramach procedury non-entrant, wywołanie mogłoby pozostawić strukturę danych w stanie, który mógłby przerwać obliczenia z punktu widzenia rozmówcy.

Fragment kodu może być ponownie włączony, ale nie jest bezpieczny dla wątków, jeśli może dokonać nieatomowej zmiany na współdzieloną (i udostępnialną) strukturę danych, która może zostać przerwana w środku aktualizacji, pozostawiając strukturę danych w stanie niespójnym. W tym przypadku inny wątek uzyskujący dostęp do struktury danych może mieć wpływ na częściowo zmienioną strukturę danych i albo zawiesić lub wykonać operację, która uszkodzi dane.

 21
Author: ConcernedOfTunbridgeWells,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-06 08:19:51

Ten artykuł mówi:

"funkcja może być reentrantowa, bezpieczna dla wątku, obie lub żadna z nich."

Jest też napisane:

"Funkcje Niecentrujące są niebezpieczne dla wątków".

Widzę, jak może to spowodować zamieszanie. Oznacza to, że standardowe funkcje udokumentowane jako niewymagane do ponownego włączenia również nie muszą być bezpieczne dla wątków, co jest prawdą w przypadku bibliotek POSIX iirc (a POSIX deklaruje to również w przypadku bibliotek ANSI / ISO, ISO nie ma pojęcia o wątkach i stąd Żadna koncepcja bezpieczeństwa nici). Innymi słowy, "jeśli funkcja mówi, że nie jest reentrantowa, to mówi, że jest również niebezpieczna dla wątku". To nie jest logiczna konieczność, to tylko konwencja.

Oto jakiś pseudo-kod, który jest bezpieczny dla wątków (cóż, jest mnóstwo okazji dla wywołań zwrotnych do tworzenia blokad z powodu inwersji blokowania, ale załóżmy, że dokumentacja zawiera wystarczające informacje dla użytkowników, aby tego uniknąć), ale nie ponownie. Ma to na celu zwiększenie globalny licznik, i wykonać callback:

take_global_lock();
int i = get_global_counter();
do_callback(i);
set_global_counter(i+1);
release_global_lock();

Jeśli wywołanie zwrotne wywoła tę procedurę ponownie, w wyniku innego wywołania zwrotnego, wtedy oba poziomy wywołania zwrotnego otrzymają ten sam parametr (który może być OK, w zależności od API), ale licznik zostanie zwiększony tylko raz (co prawie na pewno nie jest API, które chcesz, więc będzie musiał zostać zablokowany).

Zakładając, że zamek jest rekurencyjny, oczywiście. Jeśli blokada nie jest rekurencyjna, to oczywiście kod nie jest reentrantowy tak czy siak, skoro drugie zajęcie zamka nie zadziała.

Oto jakiś pseudo-kod, który jest "słaby", ale nie bezpieczny dla wątków:

int i = get_global_counter();
do_callback(i);
set_global_counter(get_global_counter()+1);

Teraz można wywołać funkcję z wywołania zwrotnego, ale nie jest bezpiecznie wywoływać funkcję jednocześnie z różnych wątków. Nie jest również bezpieczne wywołanie go z funkcji obsługi sygnału, ponieważ ponowne wejście z funkcji obsługi sygnału może również przerwać liczenie, jeśli sygnał zdarzył się w odpowiednim czasie. Więc kod nie jest re-entrant według właściwej definicji.

Oto kod, który prawdopodobnie jest w pełni re-entrant (chyba, że standard rozróżnia reentrant i 'non-interruptible by signals', i nie jestem pewien, gdzie to spada), ale nadal nie jest bezpieczny dla wątków: {]}

int i = get_global_counter();
do_callback(i);
disable_signals(); // and any other kind of interrupts on your system
set_global_counter(get_global_counter()+1);
restore_signal_state();

W aplikacji jednowątkowej jest to w porządku, zakładając, że system operacyjny obsługuje wyłączanie wszystkiego, co należy wyłączyć. Zapobiega to ponownemu wejściu w punkcie krytycznym. W zależności od tego, w jaki sposób sygnały są wyłączone, może to być bezpieczne wywołanie z obsługi sygnału, chociaż w tym konkretnym przykładzie nadal występuje problem z parametrem przekazanym do wywołania zwrotnego, który jest taki sam dla oddzielnych połączeń. Jednak nadal może pójść źle wielowątkowy.

W praktyce, non-thread-safe często oznacza non-re-entrant, ponieważ (nieformalnie) wszystko, co może pójść nie tak z powodu przerwania wątku przez scheduler, a funkcja wywołana ponownie z innego wątku, może również pójść nie tak, jeśli wątek zostanie przerwany przez sygnał, i funkcja jest wywoływana ponownie z obsługi sygnału. Ale wtedy "fix", aby zapobiec sygnałom (wyłączanie ich) różni się od "fix", aby zapobiec współbieżności (zazwyczaj blokady). To jest w najlepszym razie zasada kciuka.

Zauważ, że sugerowałem tu globale, ale dokładnie te same rozważania miałyby zastosowanie, gdyby funkcja przyjmowała jako parametr wskaźnik do licznika i blokadę. Chodzi o to, że różne przypadki byłyby niebezpieczne lub nieodwracalne po wywołaniu z tym samym parametrem, a nie kiedy w ogóle dzwonisz.

 8
Author: Steve Jessop,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-10 03:10:08