2012-02-02 8 views
15

Kiedyś wszystkie funkcje ponownego wprowadzania są wątkowane. Ale czytam Reentrancy page in Wiki, to księguje kod, który jest „doskonale wklęsłego, ale nie bezpieczny wątku. Ponieważ nie zapewniają dane globalny jest w stanie spójnym podczas wykonywania”Dlaczego ten kod jest ciągły, ale nie wątkowy

int t; 

void swap(int *x, int *y) 
{ 
     int s; 

     s = t; // save global variable 
     t = *x; 
     *x = *y; 
     // hardware interrupt might invoke isr() here! 
     *y = t; 
     t = s; // restore global variable 
} 

void isr() 
{ 
     int x = 1, y = 2; 
     swap(&x, &y); 
} 

nie rozumiem swoje wyjaśnienie . Dlaczego ta funkcja nie jest bezpieczna dla wątków? Czy to dlatego, że zmienna globalna int t zostanie zmieniona podczas wykonywania wątków?

+0

Ten przykład jest bardziej niż wymyślny. Ale powtórne wprowadzenie i zabezpieczenie wątków to pojęcia ortogonalne. –

+1

Posix ma inną definicję ponownego przynależności "W POSIX.1c," funkcja ponownego wstępu "jest zdefiniowana jako" funkcja, której efekt, wywołany przez dwa lub więcej wątków, jest zagwarantowany tak, jakby każdy z wątków wykonywał funkcję jeden po drugim w niezdefiniowanej kolejności, nawet jeśli faktyczne wykonanie jest przeplatane ", co (całkiem niezły) przykład na wikipedii nie byłby zgodny z – nos

+0

Wydaje mi się, że ten przykład nie jest powtórny: przerwane' swap() ' nie wymieniaj wartości wskazywanych przez 'x' i' y' zgodnie z oczekiwaniami ('* y' może być ustawione na 2 niezależnie od początkowej wartości' * x'). – rom1v

Odpowiedz

4

Aby dać bardziej ogólną odpowiedź, przywracanie jest tylko na poziomie funkcji. Oznacza to, że jedno wywołanie funkcji nie zmienia stanu, w którym może zmienić funkcjonowanie drugiego połączenia.

W podanym przykładzie zmienna globalna nie zostanie zmieniona między dwoma wywołaniami funkcji. Co dzieje się wewnątrz funkcji, nie ma wpływu na każde wywołanie funkcji.

Przykładem non funkcji wklęsłego jest strtok

Jest to na przykład nie można zagnieździć 2 parsowanie pętle z nim:

/* To read a several lines of comma separated numbers */ 
char buff[WHATEVER], *p1, *p2; 

    p1 = strtok(buff, "\n"); 
    while(p1) { 
    p2 = strtok(p1, ","); 
    while(p2) { 
     atoi(p2); 
     p2 = strtok(NULL, ","); 
     } 
    } 
    p1 = strtok(NULL, "\n"); 
    } 

To nie działa, ponieważ stan zewnętrznej Pętla strtok jest blokowana przez drugie wywołanie (trzeba użyć wariantu z powtórkami strtok_r).

+3

Pytanie dotyczyło wyjaśnienia, w jaki sposób funkcje zwrotne mogą być nie jest bezpieczna dla wątków Nie jest to odpowiedź na pytanie, jest jedynie przykładem odwrotności – Jed

0

W ten sposób funkcja mes mesi ze zmienną globalną o nazwie t z jakiegoś dziwacznego powodu. Jeśli ta funkcja zostanie wywołana z dwóch różnych wątków w tym samym czasie, możliwe, że otrzymasz nieoczekiwane, niepoprawne wyniki, ponieważ jedna instancja zastąpi wartość wt, która została napisana przez inne wystąpienie.

1

Jeśli wykonywałeś 2 wystąpienia (każdy na innym wątku), możesz stanąć na palcach drugiego: jeśli ktoś został przerwany przy komentarzu "przerwanie sprzętowe", a inny został wykonany, może zmienić t, aby przejście z powrotem na pierwsze sprawiłoby, że wynik byłby nieprawidłowy.

4

Sztuczka z tym rodzajem przywrócenia polega na tym, że wykonanie pierwszego połączenia zatrzymuje się podczas wykonywania drugiego połączenia. Podobnie jak wywołanie podfunkcji. Pierwsze połączenie jest kontynuowane po całkowitym zakończeniu drugiego połączenia. Ponieważ funkcja zapisuje stan t przy wejściu i przywraca go przy wyjściu, nic się nie zmieniło dla pierwszego połączenia, gdy jest kontynuowane. Dlatego zawsze masz zdefiniowaną i ścisłą kolejność wykonywania, bez względu na to, gdzie dokładnie przerwano pierwsze połączenie.

Gdy ta funkcja działa w wielu wątkach, wszystkie wykonania wykonywane są równolegle, nawet w paralele z procesorem wielordzeniowym. Nie ma zdefiniowanej kolejności wykonywania we wszystkich wątkach, tylko w jednym wątku. Tak więc wartość t może być zmieniona w dowolnym momencie przez jeden z pozostałych wątków.

+0

Zatem przywrócenie nie obejmuje równoległości? na przykład, foo() {a(); b()}, gdy pojawia się powtórność , może to być tylko (ab) b, ale może " t a a b b, który może wielowątkowych? –

+0

Zasadniczo tak. Ponownym określeniem tutaj jest zawsze (a2 b2), ponieważ wywołanie wewnętrzne jest zawsze wykonywane całkowicie przed dalszym wywoływaniem zewnętrznym. W wielowątkowości, zależy to od harmonogramu wątków i może być równie dobrze a1 a2 b1 b2. Utworzenie reentrantu funkcji nie oznacza, że ​​jest bezpieczne dla wątków. I w drugą stronę: sprawienie, by była bezpieczna dla wątków, nie oznacza, że ​​jest to powrót. Oba muszą być traktowane osobno, zawsze chodzi o to, czy i jak stan (tj. Zmienne) jest dzielony między wywołaniami i między wątkami. – Secure

+0

Tylko na uwagę. Dla mnie naprawdę trudno było sobie wyobrazić * "ponowne wejście tylko z tej samej nitki" * sytuacja. Ponieważ boję się heisenbug, nauczyłem się implikować równoległe wykonywanie wątków. I widziałem termin * przywiązanie * tylko z wątku związanego z tekstem. Myślę, że to samo dotyczy osób, które zaczęły programować po tym, jak prewencyjne wielowątkowość stała się powszechna. Ale, jak na ironię, jest to klucz do zrozumienia tego terminu. – Eonil

2

Postaram się zaproponować inny (być może mniej wymyślny) przykład funkcji, która jest wielokrotna, ale nie wątkowa.

Oto realizacja „Wieże Hanoi”, za pomocą udostępnionego globalnej „Temp” stack:

stack_t tmp; 

void hanoi_inner(stack_t src, stack_t dest, stack_t tmp, int n) 
{ 
    if (n == 1) move(src, dest) 
    else { 
    hanoi_inner(src, tmp, dest, n - 1); 
    move(src, dest); 
    hanoi_inner(tmp, dest, src, n - 1); 
    } 
} 

void hanoi(stack_t src, stack_t dest, int n) { hanoi_inner(src, dest, tmp, n); } 

Funkcja hanoi() jest reentrant ponieważ pozostawia stan bufora globalnej tmp niezmienione, gdy go zwraca (jedno zastrzeżenie: zwykłe ograniczenie posiadania rosnącego rozmiaru dysków na tmp może zostać naruszone podczas wywołania zwrotnego.) Jednak hanoi() nie jest bezpieczny dla wątków.

Oto przykład, który jest zarówno bezpieczny wątku i wklęsłego jeżeli operator przyrost n++ jest atomowy:

int buf[MAX_SIZE]; /* global, shared buffer structure */ 
int n;    /* global, shared counter */ 

int* alloc_int() { return &buf[n++]; } 

Naprawdę mógłby to wykorzystać jako podzielnika dla komórek jedna liczba całkowita (nie sprawdzić przepełnienie; wiem). Jeśli n++ nie jest operacją atomową, dwa wątki lub dwa wywołania zwrotne mogą łatwo zostać przypisane do tej samej komórki.

+1

Ten jest tylko powtórnie, jeśli n ++ jest atomowy (co zwykle nie jest). W przeciwnym razie oba wywołania mogłyby zakończyć się zwracaniem tego samego wskaźnika. –

+0

@per Dzięki - zamierzam spróbować zmienić ją tak, aby była reentrantowa bez uciekania się do blokowania lub atomowej n ++ – gcbenison

3

Załóżmy wątku i nici wątku B. A ma dwie zmienne lokalne, a = 5, B = 10 gwintu B ma dwie zmienne lokalne P = 20, Q = 30

nawlec połączenia: wymiany (& a, & b);

Wątek B połączenia: zamiana (& p, & q);

Zakładam, że oba wątki działają na różnych rdzeniach i należą do tego samego procesu. Zmienna t jest globalna i int x, int y są lokalne dla danej funkcji. Poniższe szeregowanie wątków pokazuje, w jaki sposób wartość "t" może się różnić w zależności od planowania wątków, a co za tym idzie, czy wątek kodu jest niebezpieczny. Powiedz globalne t = 100;

Thread A   Thread B 
1) int s;  int s; 
2) s = 100;  s = 100; 
3) t = 5;  no operation(nop); 
4) nop;   t = 20; // t is global so Thread A also sees the value as t = 20 
5) x = 10;  x = 30; 
6) y = 20;  y = 20; // Thread A exchange is wrong, Thread B exchange is OK 

Teraz spróbuj wyobrazić sobie, co by się stało, gdyby wyciągi 3 i 4 były w innej kolejności powyżej. t następnie otrzyma wartość 5, a wymiana w wątku B będzie błędna. Sytuacja jest jeszcze łatwiejsza, jeśli dwa wątki znajdują się na tym samym procesorze. Wtedy żadna z powyższych operacji nie będzie jednoczesna. Właśnie pokazałem przeplatanie w krokach 3 i 4, ponieważ są to najważniejsze.

Powiązane problemy