Funkcja pthread_join w języku C z pojedynczymi i wieloma przykładami wątków

Funkcja pthread_join w języku C z pojedynczymi i wieloma przykładami wątków
Język C oferuje możliwość opracowania programów wielozadaniowych przy użyciu biblioteki PTHREAD standardu POSIX. Ta metoda zwiększa prędkość gotowego programu, wykonując wiele wątków równolegle z główną funkcją.

Aby utworzyć udany program za pomocą tej metody programowania, musisz podać szczególne rozważania. Na przykład, jeśli czasy wykonywania wątku są dłuższe niż w funkcji Main () i jest on w pełni wykonany, program zakończy.

Może to powodować krytyczne problemy, ponieważ wątek, który nieoczekiwanie kończy się kończąc na zapisywaniu danych generowanych przez użytkownika, zapisywania na plik lub urządzeniu lub wykonywaniu innego ważnego zadania.

W tym artykule z podpowiedzi Linux dowiesz się, jak korzystać z funkcji pthread_join (), aby upewnić się, że jeden lub więcej wątków jest niezawodnie zakończone przed wyjściem lub wznowieniem programu.

Składnia funkcji pthread_join () w języku c

int pthread_join (pthread_t wątek, void ** retval);

Opis funkcji pthread_join () w języku c

Funkcja pthread_join () czeka na proces, z którego jest wywoływana, dopóki wątek nie zakończy zadania. Ta funkcja zawiesza funkcję, z której jest wywoływana, dopóki podproces jest określony przez jego identyfikator w argumencie wejściowego wątku.

Gdy wątek zakończy zadanie, program wznawia wykonanie z następnego wiersza kodu, który wywołuje pthread_join ().

Gdy podprzestanie podprocesowe, funkcja pthread_join () zwraca status wyjścia wątku w argumencie Retval.

Następnie widzimy argumenty wejściowe do pthread_join () wraz z opisem funkcji, która spełnia każdy z tych argumentów:

nitka: Ten argument wejściowy jest wartością danych typu pthread_t i jest identyfikatorem podprocesów, którego funkcja pthread_join () powinna oczekiwać.

Wartość identyfikatora jest generowana przez system i jest uzyskiwana w wyniku funkcji pthread_create () w wątku argumentu wyjściowego po utworzeniu wątku.

Retval: Ten argument wejściowy jest wskaźnikiem do (void *), w którym funkcja pthread_join () przechowuje status wyjścia wątku.

Jeśli pthread_join () powróci pomyślnie, zwraca 0 w wyniku. Jeśli wystąpi błąd, funkcja ta zwraca liczbę całkowitą o wartości, która reprezentuje wystąpił błąd. Później zobaczysz specjalną sekcję, która opisuje wszystkie błędy, które ta funkcja może wygenerować i jak je zidentyfikować.

Funkcja pthread_join () jest zdefiniowana w pthread.H Nagłówek. Aby go użyć, następujące nagłówki muszą być zawarte w „.plik C ”, jak pokazano następująco:

#włączać
#włączać

Uwaga: Ważne jest, aby używać tej funkcji tylko w razie potrzeby, ponieważ możesz przedłużyć czas wykonywania programu, zatrzymując jeden proces, aby poczekać na zakończenie drugiego.

Błędy kompilacji w programach z wątkami

Podczas kompilacji programów GCC, które używają wątków, kompilacja może się nie powieść, jeśli nie zostanie wykonana poprawnie z wiersza poleceń.

Najczęstszy komunikat o błędzie wydawany przez kompilator stwierdza, że ​​jedna z funkcji wątków, które odnosimy się w kodzie.

Błędy te często powodują marnowanie cennego czasu na sprawdzanie nagłówków, które włożyliśmy do kodu, ich integralności i katalogów powiązanych z kompilatorem, ponieważ wszystko wskazuje, że problem jest tam problem.

Chociaż funkcje, które powodują błąd, są zdefiniowane w „pThread.H ”nagłówek i są zawarte w kodzie, kompilator nie rozpoznaje tych funkcji, chyba że biblioteka pthread jest wywoływana z wiersza poleceń podczas kompilacji.

Na poniższej ilustracji możesz zobaczyć prawidłowy sposób wywołania biblioteki PTHREAD (podświetlonej na zielono) z konsoli poleceń podczas kompilacji programów z wątkami:

~ $ gcc -pthread ścieżka/nazwa pliku.C -O out_name

Jak widać na poniższym rysunku, błąd znika, gdy biblioteka pthread jest wywoływana podczas kompilacji.

Jak użyć funkcji pthread_join (), aby poczekać na zakończenie wykonania w języku C

W tym przykładzie tworzymy wątek, który ma dłuższy czas wykonywania niż funkcja main (). Po utworzeniu wątku używamy pthread_join (), aby poczekać na całe wykonanie wątku przed wyjściem z funkcji Main () i zakończenia programu.

W tym celu tworzymy funkcję Thread_F (), która wykonuje wątek. Ta funkcja jest pętlą „dla” z 5 cykli o jednej sekundzie, z których każdy drukuje „Sekundy do końca wątku:”Wiadomość, a następnie liczba pozostałych sekund w konsoli dowodzenia.

Z funkcji main () tworzymy Thread_1 z funkcją pthread_create (). Następnie nazywamy funkcję pthread_join (), aby poczekać, aż podproces ukończy wykonanie przed wyjściem z programu.

Wywołanie funkcji pthread_join () jest zgodne z identyfikatorem Thread_1 jako pierwszego argumentu wejściowego i retval jako zerowego w drugim argumencie. W tym przykładzie widzimy następujący pełny kod:

#włączać
#włączać
#włączać
#włączać
void* Thread_F (void* n);
int main ()

pThread_T Thread_1;
pthread_create (& Thread_1, NULL, Thread_F, null);
pthread_join (Thread_1, null);
powrót 0;

void* Thread_F (void* n)

dla (int a = 5; a!= 0; A--)

printf („sekundy do końca wątku: %i \ n”, a);
sen (1);

printf („koniec wątku \ n”);
pthread_exit (n);

Poniższy obraz pokazuje kompilację i wykonanie tego kodu:

Jak widzieliśmy na rysunku, pętla „for” zakończyła 5 cykli, a program jest zamknięty po zakończeniu wykonania Thread_1. Zobaczmy, co się stanie, jeśli usuniemy funkcję pthread_join (). Skompiluj i uruchom następujący kod:

Jak widać na rysunku, pętla „for” Thread_1 nie mogłaby zakończyć jednego przejścia, zanim funkcja main () zostanie w pełni wykonana, a program zostanie zakończony.

Jak użyć funkcji pthread_join (), aby poczekać na ukończenie wykonania w języku C

W tym przykładzie pokażemy, jak użyć funkcji pthread_join (), aby czekać na wypełnienie wielu współbieżnych wątków.

Aby to zrobić, używamy kodu z poprzedniego przykładu i dodajemy drugi wątek, który jest Thread_2 i drugą procedurę, która jest Thread_F_2 (), która wykonuje się równolegle z Thread_1. Czas wykonywania tej rutyny jest dwa razy dłuższy niż czas Thread_F_1 (), który zajmuje 10 sekund.

Metodą, której używamy, jest utworzenie dwóch wątków jeden po drugiej za pomocą funkcji pthread_create (). Następnie najpierw wywołujemy funkcję pthread_join () z najkrótszym bieżącym wątkiem, a następnie nowe wywołanie tej funkcji z najdłużej działającym wątkiem. Poniżej znajduje się pełny kod tego przykładu:

#włączać
#włączać
#włączać
#włączać
void* Thread_F_1 (void* n);
void* Thread_F_2 (void* n);
int main ()

pThread_T Thread_1;
pthread_t Thread_2;
pthread_create (& Thread_1, NULL, Thread_F_1, null);
pthread_create (& Thread_2, NULL, Thread_F_2, NULL);
pthread_join (Thread_1, null);
pthread_join (Thread_2, null);
powrót 0;

void* Thread_F_1 (void* n)

dla (int a = 5; a!= 0; A--)

printf („sekundy do końca wątku 1: %i \ n”, a);
sen (1);

printf („koniec wątku 1 \ n”);
pthread_exit (n);

void* Thread_F_2 (void* n)

dla (int a = 10; a!= 0; A--)

printf („sekundy do końca wątku 2: %i \ n”, a);
sen (1);

printf („koniec wątku 2 \ n”);
pthread_exit (n);

Jak widać na poniższym obrazie, oba wątki zakończyły ich wykonanie:

Błędy, które funkcja pthread_join () może zwrócić: co one są i jak można je wykryć?

Gdy wystąpi błąd, funkcja pthread_join () zwraca jeden z następujących predefiniowanych kodów:

ESRCH: Określony identyfikator nie jest powiązany z żadnym wątkiem.

Einval: Wątek nie można dołączyć lub inny wątek już czeka, aby dołączyć do tego.

EDEADLK: Wykryto katastrofę.

Te błędy są predefiniowane w „Errno.H ”nagłówek i są zwracane w wyniku wyjściowej liczby całkowitej funkcji pthread_join (), a nie w globalnej zmiennej errno.

Poniżej znajduje się kod z poprzedniego przykładu, w którym dodajemy warunek „jeśli”, aby ustalić, czy wystąpił błąd. Jeśli tak, program wprowadza warunek przełącznika, który identyfikuje błąd i wyświetla komunikat w konsoli polecenia z określonym opisem tego błędu.

Uwzględniamy również „errno.H ”nagłówek i zadeklaruj zmienną błędu, która będzie warunkami„ jeśli ”i„ przełączają ”.

Aby wygenerować błąd, odnosimy się do Thread_2 w argumencie wejściowego wątku funkcji pthread_join (), która nie została utworzona.

#włączać
#włączać
#włączać
#włączać
#włączać
void* Thread_F (void* n);
int main ()
w terrorze;
pThread_T Thread_1;
pthread_t Thread_2;
pthread_create (& Thread_1, NULL, Thread_F, null);
error = pthread_join (Thread_2, null);
if (błąd!= 0)
przełącznik (błąd)
Case ESRCH:
printf („Identyfikator nie jest powiązany z żadnym wątkiem \ n”);
przerwa;
Case einval:
printf („wątek nie można dołączyć lub inny wątek już czeka \ n”);
przerwa;
Case Edeadlk:
printf („Wykryto katastrofę.\N");
przerwa;

powrót 0;

void* Thread_F (void* n)

dla (int a = 5; a!= 0; A--)

printf („sekundy do końca wątku: %i \ n”, a);
sen (1);

printf („koniec wątku \ n”);
pthread_exit (n);

Poniższy obraz pokazuje kompilację i wykonanie tego kodu. Zobacz, w jaki sposób program wprowadza instrukcję „IF” i instancję ESRCH instrukcji przełącznika, która wskazuje, że identyfikator Thread_2 wskazuje na nieistniejącą wątek. Możesz także zobaczyć, że wykonanie Thread_1 jest niekompletne.

Wniosek

W tym artykule z podpowiedzi Linux pokazaliśmy, jak zaimplementować funkcję pthread_join () z biblioteki wątków, aby zapewnić pełne wykonanie podproces.

Wyjaśniliśmy składnię tej funkcji i opisaliśmy każdy z jej argumentów wejściowych i sposób uzyskania danych, które należy w nich przekazać. Następnie zaimplementowaliśmy tę funkcję w praktycznym przykładzie z fragmentami kodowymi i obrazami, aby pokazać, w jaki sposób możesz użyć funkcji pthread_join (), aby zapewnić pełne i niezawodne wykonywanie wątku.

Pokazaliśmy również, jak poprawnie połączyć bibliotekę wątków do kompilacji bezbłędnej. W specjalnej sekcji pokazaliśmy, jak wykryć i zidentyfikować błędy, które może wygenerować funkcja pthread_join ().