2014-09-22 20 views
6

Podczas korzystania z wątków POSIX, istnieje sposób "ochrony" głównego wątku przed błędami (takimi jak dereferencje wskaźników zerowych, dzielenie przez zero itd.) Spowodowanych przez wątki robocze. Przez "wątek roboczy" mam na myśli wątek posix stworzony przez pthread_create().Ochrona głównego wątku przed błędami w wątku roboczym

Niestety, nie możemy używać wyjątków - tak nie "złapać", itp

Tu jest mój program testowy (C++):

void* workerThreadFunc(void* threadId) { 
    int* a = NULL; 
    *a = 5; //Error (segmentation fault) 
    pthread_exit(NULL); 
} 

int main() { 
    cout << "Main thread start" << endl; 

    pthread_t workerThread; 
    pthread_attr_t attr; 
    pthread_attr_init(&attr); 
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); 
    pthread_create(&workerThread, &attr, workerThreadFunc, (void*)0); 
    pthread_join(workerThread, NULL); 

    cout << "Main thread end" << endl; 
} 

W powyższym przykładzie, błąd spowodowany przez workerThread będzie zakończyć cały program. Ale chciałbym, aby główny wątek nadal działał pomimo tego błędu. Czy można to osiągnąć?

+0

"Niestety, nie możemy używać wyjątków - więc nie ma" złapania "itp." - oczywiście że możesz. Wyjątek zostanie zgłoszony w wątku, który go podniósł. –

+0

Najlepszy sposób, aby "chronić" inne wątki przed wyjątkami/błędy, aby je naprawić, aby się nie pojawiły, nie zostały wykryte i nie były obsługiwane/rejestrowane/cokolwiek. –

+0

Uwaga - w powyższym przykładzie próbujesz dołączyć do wątku, który nie został jeszcze utworzony. –

Odpowiedz

8

Brzmi dla mnie tak, jakbyś używał wielu procesów, a nie wątków. Niezależne procesy są automatycznie chronione przed tego rodzaju błędami występującymi w innych procesach.

Możesz używać potoków lub pamięci współdzielonej (lub innych form IPC) do przekazywania danych między wątkami, co ma dodatkową zaletę polegającą na dzieleniu się tylko pamięcią, którą chcesz udostępnić, więc błąd w wątku roboczym nie może przejść na stosie głównego "wątku", ponieważ jest to oddzielny proces z oddzielną przestrzenią adresową.

Nici mogą się przydać, ale mają kilka wad, czasem bardziej odpowiednie są oddzielne procesy.

+0

Przez "proces" masz na myśli le fork()? – Martin

+0

Tak, 'fork()' nowe procesy potomne od wzorca i albo 'exec()' inny plik wykonywalny dla pracowników, albo po prostu kontynuuj uruchamianie tego samego pliku wykonywalnego, ale dzieci działają w trybie "pracownika". –

4

Zakładając, że system wykorzystuje sygnały w POSIX rodzaju sposób (choć, które mogą podlegać regule „nie wyjątki”), a następnie POSIX mówi:

W czasie generowania, determinacja powinna być wykonane, czy sygnał został wygenerowany dla procesu, czy dla określonego wątku w procesie. Sygnały generowane przez niektóre działania przypisane do określonego wątku, takie jak błąd sprzętowy, są generowane dla wątku, który spowodował wygenerowanie sygnału.

Więc można obsługiwać SIGSEGV, SIGFPE, itp na podstawie jednej Pthread (należy jednak pamiętać, że można ustawić tylko jedną funkcję obsługi sygnału dla całego procesu). Możesz więc "ochronić" proces przed zatrzymaniem martwym przez niepowodzenie pojedynczego pthread ... do pewnego momentu. Problem polega oczywiście na tym, że bardzo trudno jest stwierdzić, w jakim stanie znajduje się proces - niepowodzenie pthread i wszystkie inne pthready. Uszkodzony pthread może posiadać pewną liczbę muteksów. Nie powiodło się, że pthread może pozostawić pewne struktury danych w bałaganie. Kto wie, w jakie gąszczu rzeczy się znajdują - chyba że pthreads są zasadniczo niezależne. Możliwe, że inne pthready zamkną się "z gracją", zamiast rozbić się i spalić. W ostatecznym rozrachunku może być bezpieczniej zatrzymać wszystkie pthready, niż próbować kontynuować w stanie mniejszym niż dobrze zdefiniowany. Będzie to zależeć wyłącznie od natury aplikacji.

Nic nie jest na nic ... nici mogą komunikować się ze sobą łatwiej niż z procesami, a koszt ich uruchamiania jest krótszy - procesy są mniej podatne na awarię innych procesów.

5

Jedynym sposobem mogę myśleć w ten sposób rejestruje obsługi sygnału, który mógłby zamiast przerywać działanie programu, należy anulować aktualnie uruchomiony wątek, coś takiego:

void handler(int sig) 
{ 
    pthread_exit(NULL); 
} 

signal(SIGSEGV, handler); 

Note, jednak jest to niebezpieczne jak pthread_exit nie jest wymienione jako jedno z bezpiecznych wywołań systemowych wewnątrz procedury obsługi sygnału. To może zadziałać i może nie, w zależności od O/S, z którego korzystasz, i od tego, jaki sygnał sobie poradzisz.

+0

Dzięki. Wydaje się, że to wystarczy, przynajmniej na razie w prosty test, będę musiał zbadać kwestie bezpieczeństwa, o których mówisz – Martin

+0

Wygląda na to, że pthread_exit powoduje SIGABRT, jeśli obiekt został utworzony na tym stosie wątków i ten obiekt ma zdefiniowany destruktor, co spowoduje nieskończoną pętlę Zablokowałeś SIGABRT (jeśli złapałeś ich w westchnieniu), albo kompletną awarię programu, co dzieje się na przykład wtedy, gdy zadeklaruję obiekt o bardzo prostej strukturze, który się zawiesza, jeśli ten obiekt ma zdefiniowany destruktor, nawet tylko ~ A() {} Ale bez zdefiniowanego niszczyciela nie jest wysyłany SIGABRT Problem pojawia się również, gdy deklaruję na przykład wektor . – Martin

Powiązane problemy