2010-09-11 3 views
23

Poniższy program pokazuje, że możemy użyć funkcji return() lub pthread_exit(), aby zwrócić zmienną void * dostępną dla zmiennej stanu pthread_join().return() versus pthread_exit() w funkcjach start pthread

(1) Czy należy preferować korzystanie z jednego nad drugim?

(2) Dlaczego korzysta z funkcji return()? Normalnie myślimy o powrocie, umieszczając wartość na stosie, ale ponieważ wątek jest zakończony, stos powinien zniknąć. Czy też stos nie zostanie zniszczony przed pthread_join()?

(3) Czy w swojej pracy widzisz duże wykorzystanie zmiennej status? Wygląda na to, że 90% kodu, który widzę, po prostu NULL określa parametr statusu. Ponieważ wszystko, co zmieniło się poprzez pustkę * ptr, jest już odzwierciedlone w wątku wywołującym, nie ma sensu zwracać go. Każda nowa void * ptr zwrócony musi wskazywać na coś zdezynfekowanego przez wątek początkowy, który pozostawia odbierający wątek z obowiązkiem usunięcia go. Czy myliłem się sądząc, że zmienna statusu jest bezcelowa?

#include <iostream> 
#include <pthread.h> 

using namespace std; 

struct taskdata 
{ 
     int x; 
    float y; 
    string z; 
}; 


void* task1(void *data) 
{ 
    taskdata *t = (taskdata *) data; 

    t->x += 25; 
    t->y -= 4.5; 
    t->z = "Goodbye"; 

    return(data); 
} 

void* task2(void *data) 
{ 
    taskdata *t = (taskdata *) data; 

    t->x -= 25; 
    t->y += 4.5; 
    t->z = "World"; 

    pthread_exit(data); 
} 


int main(int argc, char *argv[]) 
{ 
    pthread_t threadID; 

    taskdata t = {10, 10.0, "Hello"}; 

    void *status; 

    cout << "before " << t.x << " " << t.y << " " << t.z << endl; 

    //by return() 

    pthread_create(&threadID, NULL, task1, (void *) &t); 

    pthread_join(threadID, &status); 

    taskdata *ts = (taskdata *) status; 

    cout << "after task1 " << ts->x << " " << ts->y << " " << ts->z << endl; 

    //by pthread_exit() 

    pthread_create(&threadID, NULL, task2, (void *) &t); 

    pthread_join(threadID, &status); 

    ts = (taskdata *) status; 

    cout << "after task2 " << ts->x << " " << ts->y << " " << ts->z << endl; 

} 

o mocy:

before 10 10 Hello 
after task1 35 5.5 Goodbye 
after task2 10 10 World 

Odpowiedz

30

(1) w kod C++, stosując return powoduje stos być zmienne odwijana i lokalne zniszczeniu podczas pthread_exit zapewniona jest tylko, aby wywołać teleskopowe anulowanie zarejestrowany pthread_cancel_push() . W niektórych systemach mechanizm ten spowoduje również wywołanie destruktorów dla zmiennych lokalnych C++, ale nie gwarantuje to przenośnego kodu --- sprawdź dokumentację twojej platformy.

Również w main(), return niejawnie nazywać exit(), a tym samym zakończyć program, natomiast pthread_exit() będzie jedynie zakończyć wątek, a program pozostanie uruchomiony, dopóki wszystkie wątki zostały zakończone lub jakiś wątek wywołuje exit(), abort() lub innej funkcji kończący program.

(2) Używanie return działa, ponieważ specyfikacja POSIX tak mówi. Zwrócona wartość jest przechowywana w miejscu, w którym może ją odzyskać pthread_join(). Zasoby używane przez wątek nie są odzyskiwane, dopóki nie zostaną wywołane.

(3) Nigdy nie używam zwracanej wartości wątku w nieprzetworzonych wątkach POSIX. Jednak mam tendencję do używania obiektów o wyższym poziomie, takich jak biblioteka wątków Boost, a ostatnio biblioteka wątków C++ 0x, która zapewnia alternatywne sposoby przesyłania wartości między wątkami, takie jak futures, które pozwalają uniknąć problemów związanych z zarządzaniem pamięcią, które nawiązać do.

+1

Jeśli chodzi o (1), pthread_exit nie kończy natychmiast wątku, uruchamia procedury obsługi anulowania. Przynajmniej NPTL/g ++ używa tego samego mechanizmu dla procedur obsługi anulowania i C++, więc pthread_exit faktycznie odwija ​​stos w tym przypadku. Podobnie, OpenVMS pthread_exit/anulowanie wątku rozwija stos i działa z C++. Powinieneś prawdopodobnie sprawdzić instrukcję dla Twojej konkretnej implementacji pthread w odniesieniu do zachowania pthread_exit i rozwijania stosu. –

+0

Wyjaśniłem (1), aby uwzględnić twój komentarz. –

+0

Zdecydowanie kupuję twoją książkę :) (mam nadzieję, że wkrótce wyjdzie). – celavek

6

Uważam, że najlepiej jest użyć return z start_routine, ponieważ zapewnia to odpowiednie rozwinięcie stosu wywołań.

Jest to jeszcze ważniejsze dla C niż C++, ponieważ nie posiada magii destruktora, która oczyszcza bałagan po wyjściowych wyjściach. Zatem twój kod powinien przejść przez wszystkie końcowe części procedur na stosie wywołań, aby wykonać te same operacje.

dlaczego to działa, to jest proste

Jeśli wróci start_routine, efekt będzie tak, jakby nie było niejawne wywołanie pthread_exit() przy użyciu wartości zwracanej start_routine jak stan wyjścia

Dla mojego osobistego doświadczenia mam tendencję do nieużywania statusu zakończonych wątków. Właśnie dlatego często mam wątki rozpoczęte detached. Ale to powinno w dużej mierze zależeć od aplikacji i na pewno nie można go uogólniać.