Czytelnicy tacy jak ty pomagają wspierać MUO. Kiedy dokonujesz zakupu za pomocą linków na naszej stronie, możemy otrzymać prowizję partnerską. Czytaj więcej.

W systemie Linux można tworzyć wątki i zarządzać nimi w języku C/C++ przy użyciu biblioteki wątków POSIX (pthread). W przeciwieństwie do innych systemów operacyjnych, w Linuksie istnieje niewielka różnica między wątkiem a procesem. Dlatego Linux często odnosi się do swoich wątków jako procesów lekkich.

Korzystając z biblioteki pthread, możesz tworzyć wątki, czekać na ich zakończenie i jawnie je kończyć.

Historia użycia wątków w systemie Linux

Przed Linuksem w wersji 2.6 główną implementacją wątku był LinuxThreads. Ta implementacja miała znaczne ograniczenia pod względem wydajności i operacji synchronizacji. Limit maksymalnej liczby wątków, które mogą działać, ograniczył je do 1000.

W 2003 roku zespołowi kierowanemu przez programistów z IBM i RedHat udało się stworzyć Natywna biblioteka wątków POSIX (NPTL) dostępny projekt. Został po raz pierwszy wprowadzony w RedHat Enterprise w wersji 3, aby rozwiązać problemy z wydajnością wirtualnej maszyny Java w systemie Linux. Obecnie biblioteka GNU C zawiera implementacje obu mechanizmów wątkowania.

instagram viewer

Żadne z nich nie jest implementacją zielonych wątków, którymi maszyna wirtualna zarządzałaby i działała w trybie wyłącznie użytkownika. Kiedy używasz biblioteki pthread, jądro tworzy wątek przy każdym uruchomieniu programu.

Możesz znaleźć informacje dotyczące konkretnego wątku dla dowolnego uruchomionego procesu w plikach poniżej /proc//task. Jest to standardowa lokalizacja informacji o procesie w standard linuksowy procfs. W przypadku aplikacji jednowątkowych okaże się, że w tym katalogu istnieje rekord zadania o tej samej wartości co PID.

Logika pracy wątków

Wątki są jak procesy aktualnie uruchomione w systemie operacyjnym. W systemach jednoprocesorowych (np. mikrokontrolerach) jądro systemu operacyjnego symuluje wątki. Pozwala to na równoczesne uruchamianie transakcji poprzez krojenie.

Jednordzeniowy system operacyjny może tak naprawdę uruchamiać tylko jeden proces na raz. Jednak w systemy wielordzeniowe lub wieloprocesorowe, procesy te mogą przebiegać jednocześnie.

Tworzenie wątków w C

Możesz użyć pthread_create funkcja tworzenia nowego wątku. The pthread.h plik nagłówkowy zawiera definicję sygnatury wraz z innymi funkcjami związanymi z wątkami. Wątki używają tej samej przestrzeni adresowej i deskryptorów plików, co program główny.

Biblioteka pthread zawiera również niezbędną obsługę operacji mutex i warunkowych wymaganych do operacji synchronizacji.

Gdy korzystasz z funkcji biblioteki pthread, musisz upewnić się, że kompilator łączy pliki wątek bibliotekę do pliku wykonywalnego. W razie potrzeby możesz poinstruować kompilator, aby łączył się z biblioteką za pomocą -l opcja:

gcc -o test test_wątek.c -lpwątek

Funkcja pthread_create ma następującą sygnaturę:

intpthread_create(pthread_t *nitka, konstpthread_attr_t *atrakcja, próżnia *(*start_rutyna)(próżnia *), próżnia * argument)

Zwraca 0, jeśli procedura zakończy się pomyślnie. Jeśli wystąpi problem, zwraca niezerowy kod błędu. W powyższym podpisie funkcji:

  • The nitka parametr jest typu pthread_t. Utworzony wątek będzie zawsze dostępny z tym odnośnikiem.
  • The atr parametr pozwala określić niestandardowe zachowanie. Możesz użyć szeregu funkcji specyficznych dla wątku, zaczynając od pthread_attr_ aby ustawić tę wartość. Możliwe dostosowania to zasady planowania, rozmiar stosu i zasady odłączania.
  • start_rutyna określa funkcję, którą wątek będzie uruchamiał.
  • arg reprezentuje ogólną strukturę danych przekazaną do funkcji przez wątek.

Oto przykładowa aplikacja:

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

próżnia *pracownik(próżnia *dane)
{
zwęglać * imię = (zwęglać*)dane;

Do (int ja = 0; ja < 120; i++)
{
śpisz(50000);
drukujf(„Cześć z wątku nazwa = %s\n”, nazwa);
}

drukujf("Wątek %s wykonany!\n", nazwa);
powrótZERO;
}

intgłówny(próżnia)
{
pthread_t th1, th2;
pthread_create(&th1, ZERO, pracownik, „X”);
pthread_create(&th2, ZERO, pracownik, „Y”);
spać(5);
drukujf("Wyjście z programu głównego\n");
powrót0;
}

Rodzaje gwintów

Kiedy wątek powraca z główny() funkcji w aplikacji, wszystkie wątki kończą się, a system zwalnia wszystkie zasoby używane przez program. Podobnie, wychodząc z dowolnego wątku za pomocą polecenia takiego jak an Wyjście(), twój program zakończy wszystkie wątki.

z pthread_join zamiast tego możesz poczekać na zakończenie wątku. Wątek korzystający z tej funkcji będzie blokowany do momentu zakończenia oczekiwanego wątku. Zasoby, których używają z systemu, nie są zwracane nawet w przypadkach, takich jak zakończenie dołączanych wątków, nieplanowane przez procesor lub nawet niepowodzenie połączenia z ptread_join.

Czasami zdarzają się sytuacje, w których łączenie za pomocą pthread_join nie ma sensu; jeśli nie można na przykład przewidzieć, kiedy wątek się skończy. W takim przypadku można zapewnić, że system automatycznie zwróci wszystkie zasoby w punkcie, w którym wątek powraca.

Aby to osiągnąć, należy rozpocząć odpowiednie wątki z WOLNOSTOJĄCY status. Podczas zakładania wątku ODŁĄCZYĆ status można ustawić za pomocą wartości atrybutu wątku lub za pomocą pthread_detach funkcjonować:

intpthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
intpthread_detach(pthread_t nitka);

Oto przykład użycia funkcji pthread_join(). Zamień główną funkcję w pierwszym programie na następującą:

intgłówny(próżnia)
{
pthread_t th1, th2;
pthread_create(&th1, ZERO, pracownik, „X”);
pthread_create(&th2, ZERO, pracownik, „Y”);
spać(5);
drukujf("wyjście z programu głównego\n");
pthread_join (th1, ZERO);
pthread_join (th2, ZERO);
powrót0;
}

Po skompilowaniu i uruchomieniu programu wynik będzie następujący:

Cześć z wątku Y
Witam z wątku X
Cześć z wątku Y
...
Cześć z wątku Y
wyjście z programu głównego
Witam z wątku X
...
Witam z wątku X
Wątek X zakończony!
Cześć z wątku Y
Wątek Y zakończony!

Zakończenie wątku

Możesz anulować wątek za pomocą wywołania pthread_cancel, przekazując odpowiedni pthread_t ID:

intpthread_cancel(pthread_t nitka);

Możesz zobaczyć to w akcji w poniższym kodzie. Znowu tylko tzw główny funkcja jest inna:

intgłówny(próżnia)
{
pthread_t th1, th2;
pthread_create(&th1, ZERO, pracownik, „X”);
pthread_create(&th2, ZERO, pracownik, „Y”);
spać(1);
drukujf("> Anulowanie wątku Y!!\n");
pthread_cancel (th2);
śpisz(100000);
drukujf("> Anulowanie wątku X!\n");
pthread_cancel (th1);
drukujf("wyjście z programu głównego\n");
powrót0;
}

Dlaczego tworzone są wątki?

Systemy operacyjne zawsze próbują uruchamiać wątki na jednym lub większej liczbie procesorów, albo z utworzonej przez siebie listy, albo z listy wątków utworzonej przez użytkownika. Niektóre wątki nie mogą działać, ponieważ czekają na sygnał wejścia/wyjścia ze sprzętu. Mogą również czekać dobrowolnie, czekać na odpowiedź z innego wątku lub blokować ich inny wątek.

Możesz dostosować zasoby przydzielane do wątków tworzonych za pomocą pthread. Mogą to być niestandardowe zasady planowania lub w razie potrzeby można wybrać algorytmy planowania, takie jak FIFO lub Round-Robin.