WĄTKI W SYSTEMIE LINUX

Podobne dokumenty
w odróżnieniu od procesów współdzielą przestrzeń adresową mogą komunikować się za pomocą zmiennych globalnych

Temat zajęć: Tworzenie i obsługa wątków.

1. Uruchom poniższy program tworzący pojedynczy wątek:

Programowanie Współbieżne

Plan wykładu. Obliczenia równoległe w zagadnieniach inżynierskich. Wykład 1 p. Wzajemne wykluczanie. Procesy współbieżne

Tworzenie wątków. #include <pthread.h> pthread_t thread;

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.4

Plan wykładu. Programowanie aplikacji równoległych i rozproszonych. Wykład 1 p. Wzajemne wykluczanie. Procesy współbieżne

Programowanie aplikacji równoległych i rozproszonych. Wykład 1

Programowanie równoległe i rozproszone. W1. Wielowątkowość. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Programowanie równoległe i rozproszone. Monitory i zmienne warunku. Krzysztof Banaś Programowanie równoległe i rozproszone 1

Mariusz Rudnicki PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.4

Systemy Operacyjne 2: Wątki pthreads. dr inż. Arkadiusz Chrobot

procesy odrębne dzielone

Procesy i wątki. Krzysztof Banaś Obliczenia równoległe 1

Wątki. S. Samolej: Wątki 1

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Wykład 3. Procesy i wątki. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Przetwarzanie wielowątkowe przetwarzanie współbieżne. Krzysztof Banaś Obliczenia równoległe 1

Dzisiejszy wykład. Klasa string. wersja prosta wersja ze zliczaniem odwołań. Wyjątki Specyfikator volatile Semafory

Implementacje zgodne z tym standardem są nazywane wątkami POSIX lub Pthreads.

Problemy współbieżności

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Programowanie współbieżne Wykład 9 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Współbieżność w środowisku Java

Wielowątkowy serwer TCP

Podstawy programowania współbieżnego. 1. Wprowadzenie. 2. Podstawowe pojęcia

Poniższe funkcje opisane są w 2 i 3 części pomocy systemowej.

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Autor: dr inż. Zofia Kruczkiewicz, Programowanie aplikacji internetowych 1

Problemy czytelników i pisarzy oraz 5 ucztujących filozofów

Programowanie w Sieci Internet. Python: Wątki. Kraków, 12 grudnia 2014 r. mgr Piotr Rytko Wydział Matematyki i Informatyki

Programowanie współbieżne Wykład 10 Synchronizacja dostępu do współdzielonych zasobów. Iwona Kochańska

UŻYCIE I ZARZĄDZANIE WĄTKAMI

Wielozadaniowość w systemie Microsoft Windows

Pobieranie argumentów wiersza polecenia

Stworzenie klasy nie jest równoznaczne z wykorzystaniem wielowątkowości. Uzyskuje się ją dopiero poprzez inicjalizację wątku.

UŻYCIE I ZARZĄDZANIE WĄTKAMI

Współbieżność i równoległość w środowiskach obiektowych. Krzysztof Banaś Obliczenia równoległe 1

z powielaniem wielu struktur danych oraz komunikacja

9. Problem wzajemnego wykluczania i sekcji krytycznej

METODY I JĘZYKI PROGRAMOWANIA PROGRAMOWANIE STRUKTURALNE. Wykład 02

Jędrzej Ułasiewicz Programownie aplikacji współbieżnych str. 1. Wątki

4. Procesy pojęcia podstawowe

Procesy, wątki i zasoby

Zaawansowane programowanie w C++ (PCP)

ZAJECIA Z SYSTEMÓW OPERACYJNYCH 2 (Programowanie): funkcje POSIX

Architektury systemów równoległych

Tworzenie programów równoległych. Krzysztof Banaś Obliczenia równoległe 1

Signals + Threads: Qt vs. Boost

Wykład 5. Synchronizacja (część II) Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

Kurs programowania. Wykład 8. Wojciech Macyna

Obliczenia równoległe i rozproszone w JAVIE. Michał Kozłowski 30 listopada 2003

Wątek - definicja. Wykorzystanie kilku rdzeni procesora jednocześnie Zrównoleglenie obliczeń Jednoczesna obsługa ekranu i procesu obliczeniowego

Wykład 4. Synchronizacja procesów (i wątków) cześć I. Wojciech Kwedlo, Wykład z Systemów Operacyjnych -1- Wydział Informatyki PB

4. Procesy pojęcia podstawowe

Wielowątkowość mgr Tomasz Xięski, Instytut Informatyki, Uniwersytet Śląski Katowice, 2011

Systemy operacyjne III

Synchronizacja procesów i wątków

Przeplot. Synchronizacja procesów. Cel i metody synchronizacji procesów. Wątki współbieżne

Procesy. Systemy Operacyjne 2 laboratorium. Mateusz Hołenko. 9 października 2011

Problemy czytelników i pisarzy oraz 5 ucztujących filozofów

Sygnały. 7. Sygnały (2005/2006)

Program współbieżny jest zbiorem sekwencyjnych PROGRAMOWANIE C++

Java. język programowania obiektowego. Programowanie w językach wysokiego poziomu. mgr inż. Anna Wawszczak

7. Szeregowanie procesów w systemie QNX6 Neutrino

Systemy operacyjne III

Java. Wykład. Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ

Instrukcja do laboratorium Systemów Operacyjnych. (semestr drugi)

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.2

Podstawy programowania. Wykład Funkcje. Krzysztof Banaś Podstawy programowania 1

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Tworzenie programów równoległych cd. Krzysztof Banaś Obliczenia równoległe 1

Systemy operacyjne. Zajęcia 11. Monitory

synchronizacji procesów

Prezentacja systemu RTLinux

Futex (Fast Userspace Mutex) Łukasz Białek

Problem producentakonsumenta

Mechanizmy pracy równoległej. Jarosław Kuchta

Wątki. Definiowanie wątków jako klas potomnych Thread. Nadpisanie metody run().

Podstawowe elementy proceduralne w C++ Program i wyjście. Zmienne i arytmetyka. Wskaźniki i tablice. Testy i pętle. Funkcje.

Programowanie wielowątkowe. Jarosław Kuchta

synchronizacji procesów

Temat: Dynamiczne przydzielanie i zwalnianie pamięci. Struktura listy operacje wstawiania, wyszukiwania oraz usuwania danych.

Uniwersytet Zielonogórski Instytut Sterowania i Systemów Informatycznych. Ćwiczenie 3 stos Laboratorium Metod i Języków Programowania

dynamiczny przydział pamięci calloc() memset() memcpy( ) (wskaźniki!! )

4. Procesy pojęcia podstawowe

Język Java wątki (streszczenie)

Kurs programowania. Wykład 8. Wojciech Macyna. 10 maj 2017

Podstawy programowania. Wykład 6 Wskaźniki. Krzysztof Banaś Podstawy programowania 1

Programowanie współbieżne Wykład 7. Iwona Kochaoska

Procesy, zasoby i wątki

Procesy, zasoby i wątki

Procesy, zasoby i wątki

Podstawy programowania skrót z wykładów:

Wstęp do Programowania, laboratorium 02

Gdy kilka procesów czyta a przynajmniej jeden dokonuje zapisu wynik odczytu zależeć może od sposobu realizacji przeplotu.

Od uczestników szkolenia wymagana jest umiejętność programowania w języku C oraz podstawowa znajomość obsługi systemu Linux.

Komunikacja za pomocą potoków. Tomasz Borzyszkowski

Transkrypt:

Mirosław Jedynak WĄTKI W SYSTEMIE LINUX Wstęp Wątki vs. Procesy Biblioteka: libpthread Tworzenie wątku Oczekiwanie na zakończenie innego wątku Uzyskanie własnego identyfikatora Ustawianie atrybutów przy tworzeniu wątków Zakończenie działania wątku Anulowanie wątku Dane własne wątku Mutex (MUTual Exclusion lock) Semafory dla wątków Zmienne warunków Obsługa sygnałów Debugging wątków Pytania Spis Treści Wstęp Wątki, podobnie jak procesy stanowią mechanizm pozwalający programowi na robienie naraz więcej niż jednej rzeczy. Podobnie jak procesy także wątki sprawiają wrażenie, że wykonują się jednocześnie; jądro systemu szereguje je asynchronicznie, przerywając wątki aby oddać czas innym. Idea wątków została użyta, aby przyśpieszyć pracę systemu wątki są często nazywane lekkimi procesami, ponieważ operacje potrzebne do przełączenia wątków zajmują znacznie mniej czasu i niż na przełączenie kontekstu procesów. Istnieje jednak ścisła zależność miedzy watkami a procesami wątki są jednostką podrzędna w stosunku do procesów żaden wątek nie może istnieć bez procesu nadrzędnego (ale jeden proces może mieć więcej niż jeden wątek podporządkowany) Wątki poziomu użytkownika Wątki poziomu użytkownika rezygnują z zarządzania wykonaniem przez jądro i robią to same. Często jest to nazywane "wielowątkowością spółdzielczą", gdyż proces definiuje zbiór wykonywalnych procedur, które są "wymieniane" poprzez operacje na wskaźniku stosu. Zwykle każdy wątek "rezygnuje" z procesora poprzez bezpośrednie wywołanie żądania wymiany (wysłanie sygnału i zainicjowanie mechanizmu zarządzającego) albo przez odebranie sygnału zegara systemowego. Obecnie wątki poziomu użytkownika mogą szybciej dokonać wymiany niż wątki poziomu jądra, ale ciągły rozwój tych drugich powoduje, że ta różnica jest bardzo mała. Implementacje wątków poziomu użytkownika napotykają na liczne problemy, które trudno obejść, np: problem "kradzenia" czasu wykonania innych wątków przez jeden wątek brak sposobu wykorzystania możliwości SMP oczekiwanie jednego z wątków na zakończenie blokującej operacji wejścia/wyjścia powoduje, że inne wątki tego procesu też tracą swój czas wykonania.

Różne rozwiązania tych problemów zależą najczęściej bezpośrednio od sprzętowej strony systemu: kontrola monopolizacji czasu poprzez monitor używający swojego własnego zegara, uruchamianie procesów na określonych procesorach i tam startowanie wątków, czy też nadpisywanie funkcji systemowych, żeby były wielowejściowe (nieblokujące operacje wejścia/wyjścia itp.). Wątki poziomu jądra Wątki poziomu jądra są często implementowane poprzez dołączenie do każdego procesu tabeli/listy jego wątków. W tym rozwiązaniu system zarządza każdym wątkiem wykorzystując kwant czasu przyznany dla jego procesu-rodzica. Zaletą takiej implementacji jest zniknięcie zjawiska "kradzenia" czasu wykonania innych wątków przez "zachłanny" wątek, bo zegar systemowy tyka niezależnie i system wydziedzicza "niesforny" wątek. Także blokowanie operacji wejścia/wyjścia nie jest już problemem. Ponadto przy poprawnym zakodowaniu programu, proces może automatycznie wyciągnąć korzyści z istnienia SMP przyśpieszając swoje wykonanie przy każdym dodatkowym procesorze. Kombinacje obu rozwiązań Niektóre implementacje używają obu rozwiązań: wątków poziomu użytkownika powiązanych z wątkami jądra. Takie połączenie przynosi korzyści wynikające z obu wersji wątków, choć rozwój wątków jądra spowoduje, że jedyną zaletą tego rozwiązania będzie możliwość współdziałania wielu wątków. W dalszej części prezentacja będzie poświęcona wątkom systemowym Wątki vs. Procesy Wątki współdzielą (w przeciwieństwie do procesów) Przestrzeń pamięci Deskryptory plików Inne zasoby systemowe (funkcja exec kończy wszystkie wątki, kolejki, ) Wątki nie współdzielą Stosu Własne dane wątku Biblioteka: libpthread #include <pthread.h> kompilując przy użyciu gcc programy korzystające z tej biblioteki, należy wymusić jej dołączenie, przez użycie opcji -lpthread. gcc -lpthread program.c

Tworzenie wątku Identyfikator wątku: pthread_t Kiedy wątek jest tworzony zaczyna wykonywać funkcję wątku. Po zakończeniu funkcji kończy istnienie także wątek. Argumentem funkcji tworzącej wątek oraz typem zwracanej wartości jest void*; jądro systemu nie ingeruje w treść tych danych (często jest to wskaźnik do struktury, z której wątek pobiera informacje na temat argumentów uwaga na zmienne lokalne i zwalnianie zasobów - free); Deklaracja #include <pthread.h> int pthread_create(pthread_t * thread, pthread_attr_t attr, void * (*start_routine)(void *), void * arg); Opis Funkcja pthread_create tworzy nowy watek, który wykonuje się współbieżnie z wątkiem wywołującym. Nowy watek zaczyna wykonywać podana funkcje start_routine podając jej arg jako pierwszy argument. Nowy watek kończy się explicite przez wywołanie procedury pthread_exit lub przez powrót z start_routine. Argument attr określa atrybuty nowego wątku, do których ustalenia służy funkcja pthread_attr_init. Jeśli jako attr przekażemy NULL, zostaną określone domyślne atrybuty wątku: możliwość dołączenia, domyślny sposób kolejkowania (nie czasu rzeczywistego). Po bezbłędnym wykonaniu pthread_create umieszcza identyfikator nowoutworzonego wątku w miejscu wskazywanym przez argument thread i zwraca 0. W razie błędu zwracane jest EAGAIN w przypadku, gdy brakuje zasobów systemowych żeby stworzyć proces dla nowego wątku lub, gdy więcej niż PTHREAD_THREADS_MAX wątków już działa w systemie #include <pthread.h> #include <stdio.h> /* Prints x s to stderr. The parameter is unused. Does not return. */ void* print_xs (void* unused) while (1) fputc ( x, stderr); return NULL; /* The main program. */ int main () pthread_t thread_id; /* Create a new thread. The new thread will run the print_xs function. */ pthread_create (&thread_id, NULL, &print_xs, NULL); /* Print o s continuously to stderr. */ while (1) fputc ( o, stderr); return 0;

Oczekiwanie na zakończenie innego wątku Deklaracja int pthread_join(pthread_t th, void **thread_return); Opis Funkcja pthread_join zawiesza działanie wołającego wątku aż do momentu, gdy watek identyfikowany przez th nie zakończy działania. Jeśli argument thread_return jest różny od NULL to kod zakończenia wątku th zostanie wstawiony w miejsce wskazywane przez thread_return. Kodem zakończenia może być wartość określoną przy wołaniu funkcji pthread_exit lub PTHREAD_CANCELLED jeśli watek był usunięty. Watek, do którego dołączamy musi być w stanie umożliwiającym dołączanie (nie może być odłączony przez wywołanie pthread_detach lub określenie atrybutu PTHREAD_CREATE_DETACHED przy jego tworzeniu przez pthread_create). W momencie, gdy watek w stanie umożliwiającym dołączenie kończy działanie jego zasoby (deskryptor wątku i stos) nie są zwalniane dopóki inny watek nie wykona na nim pthread_join. Dlatego pthread_join powinien być wykonany dla każdego nieodłączonego wątku. Co najwyżej jeden watek może czekać na zakończenie danego wątku. Wywołanie pthread_join w momencie, gdy jakiś inny watek już oczekuje na jego zakończenie spowoduje powrót z funkcji pthread_join z błędem. Oczekiwanie przez wywołanie funkcji pthread_join jest tzw. cancellation-point, co oznacza, ze jeśli watek zostanie odwołany (canceled) w czasie oczekiwania, to działanie wątku zostanie zakończone natychmiast bez czekania na synchronizacje z wątkiem th. W przypadku sukcesu funkcja pthread_join zwraca 0 i kod zakończenia wątku th jest umieszczany w miejscu wskazywanym przez thread_return. W przypadku błędu zwracana jest wartość EINVAL jeśli watek th już został odłączony (detached) lub inny watek już oczekuje na jego zakończenie albo wartość EDEADLK jeśli argument th odnosi się do wołającego wątku. int main () pthread_t thread1_id; pthread_t thread2_id; struct char_print_parms thread1_args; struct char_print_parms thread2_args; /* Create a new thread to print 30,000 x s. */ thread1_args.character = x ; thread1_args.count = 30000; pthread_create (&thread1_id, NULL, &char_print, &thread1_args); /* Create a new thread to print 20,000 o s. */ thread2_args.character = o ; thread2_args.count = 20000; pthread_create (&thread2_id, NULL, &char_print,

&thread2_args); /* Make sure the first thread has finished. */ pthread_join (thread1_id, NULL); /* Make sure the second thread has finished. */ pthread_join (thread2_id, NULL); /* Now we can safely return. */ return; Uzyskanie własnego identyfikatora Deklaracja pthread_t pthread_self(void); Opis pthread_self zwraca identyfikator wątku, który wywołał funkcje. Porównanie dwóch identyfikatorów wątków int pthread_equal(pthread_t thread1, pthread_t thread2); Funkcja pthread_equal określa, czy oba identyfikatory odnoszą się do tego samego wątku. Zwraca niezerowa wartość jeśli thread1 i thread2 odnoszą się do tego samego wątku. W przeciwnym przypadku 0 jest zwracane. Ustawianie atrybutów przy tworzeniu wątków Ustawić atrybutu wątku można w czasie tworzenia wątku drugi argument. 1. wywołać funkcje pthread_attr_init(pthread_attr_t tatr) 2. zmodywikowac (pthread_attr_t) attr 3. przekazac wskaźnik do pthread_create 4. wywołać funkcje pthread_attr_destroy zwolnienie zasobów związanych w funkcji pthread_attr_init 5. Obiekt atrybutów może być zmienna automatyczną i zostać użyty przy tworzeniu kilku wątków. Najczęściej używanym atrybutem jest stan odłączenia (detached state). Do ustalenia stanu odłączenia wątku służy funkcja pthread_attr_setddetachedstate żeby odłączyć należy jako drugi argument podać PTHREAD_CREATE_DETACHED.

Zakończenie działania wątku Deklaracja void pthread_exit(void *retval); Opis Funkcja pthread_exit kończy działanie wołającego wątku. Wywoływane są po kolei (LIFO) wszystkie funkcje czyszczące określone przez pthread_cleanup_push. Funkcje kończące dla wszystkich kluczy odnoszących się do konkretnego wątku i mających wartość inna niż NULL (patrz pthread_key_create) są wykonywane. Dopiero po tym wszystkim działanie wątku jest wstrzymywane. Argument retval określa kod zakończenia wątku, który może być odczytany przez inny watek za pomocą funkcji pthread_join. Anulowanie wątku Deklaracja int pthread_exit(pthread_t thread) Opis Normalnie można zakończyć wątek przez wywołanie pthread_exit albo poprzez dojście do końca funkcji wątku. Aby jeden wątek mógł przerwać inny należy użyć funkcji pthread_cancel. Anulowany wątek zwraca wartość (do pthread_join) PTHREAD_CANCELED Wątek może kontrolować kiedy i czy zostanie anulowany(zasoby zwalniane): asynchroniczne anulowanie w każdej chwili synchroniczne anulowanie żądanie anulowania są kolejkowane, aż do osiągnięcia punktu anulowanie bez możliwości anulowania Aby zmienić sposób anulowania wątku należy użyć pthread_setcanceltype z argumentami PTHREAD_CANCEL_ASYNCHRONOUS asynchroniczne. PTHRAD_CANCEL_DEFERED synchroniczne; drugim argumentem jest wskaźnik do obiektu gdzie ma zostać zachowany poprzedni stan wątku (może być NULL) Tworzenie punktów anulowania void pthread_cancel(void) Tworzenie sekcji krytycznych int pthread_setcancelstate(int state, int* oldstate) State - PTHREAD_CANCEL_DISABLE / PTHREAD_CANCEL_ENABLE,

#include <pthread.h> #include <stdio.h> #include <string.h> /* An array of balances in accounts, indexed by account number. */ float* account_balances; /* Transfer DOLLARS from account FROM_ACCT to account TO_ACCT. Return 0 if the transaction succeeded, or 1 if the balance FROM_ACCT is too small. */ int process_transaction (int from_acct, int to_acct, float dollars) int old_cancel_state; /* Check the balance in FROM_ACCT. */ if (account_balances[from_acct] < dollars) return 1; /* Begin critical section. */ pthread_setcancelstate (PTHREAD_CANCEL_DISABLE, &old_cancel_state); /* Move the money. */ account_balances[to_acct] += dollars; account_balances[from_acct] -= dollars; /* End critical section. */ pthread_setcancelstate (old_cancel_state, NULL); return 0; Dane własne wątku int ptread_key_create(pthred_key_t *key, void *destructor(void*)) Mutex (MUTual Exclusion lock) Operacje na mutexach pthread_mutex_init, pthread_mutex_lock, pthread_mutex_trylock, pthread_mutex_unlock, pthread_mutex_destroy Deklaracja pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr); int pthread_mutex_lock(pthread_mutex_t *mutex)); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_destroy(pthread_mutex_t *mutex);

Opis Mutex jest mechanizmem wzajemnego wykluczania (MUTual EXclusion) służącym do chronienia danych dzielonych miedzy watkami przed równoczesnymi modyfikacjami i używanym do implementacji sekcji krytycznych i monitorów. Mutex ma dwa możliwe stany: otwarty (nie zajęty przez żaden watek) lub zajęty (przez jeden watek). Mutex nie może być w posiadaniu więcej niż jednego wątku na raz. Watek próbujący zając mutex będący w osiadaniu innego wątku jest zawieszany aż do chwili zwolnienia mutexu przez watek, który go posiadał wcześniej. Inicjacja Funkcja pthread_mutex_init inicjalizuje obiekt mutex zgodnie z atrybutami przekazanymi przez mutexattr. Jeśli mutexattr jest NULL używane są wartości domyślne. Funkcja zawsze zwraca 0. Blokowanie/ odblowokanie Funkcja pthread_mutex_lock zajmuje dany mutex. Jeśli mutex jest wolny zostaje zajęty i przypisany wątkowi wołającemu i pthread_mutex_lock kończy działanie natychmiast. Jeśli mutex jest zajęty przez jakiś inny watek pthread_mutex_lock zawiesza działanie wątku aż do momentu, kiedy mutex zostanie zwolniony. Jeśli mutex jest już zajęty przez watek wołający to zachowanie funkcji zależy od rodzaju mutexu. Jeśli mutex dopuszcza rekurencje to funkcja kończy działanie poprawnie zapisując sobie ilość wywołań funkcji (głębokość rekurencji - potem trzeba wywołać tyle samo razy pthread_mutex_unlock żeby zwolnić mutex), jeśli zaś nie dopuszcza to doprowadza do blokady (deadlock) wątku. Funkcja zwraca błąd EINVAL jeśli mutex nie został wcześniej poprawnie zainicjalizowany. Funkcja pthread_mutex_trylock zachowuje się identyczne do pthread_mutex_lock, jednak nie jest blokującą (zwraca EBUSY w razie zajętości mutexu). Funkcja pthread_mutex_unlock zwalnia dany mutex. Mutex musi być wcześniej zajęty przez wołający proces. Jeśli mutex jest nierekurencyjny to zawsze wraca do stanu zwolnionego, jeśli jest rekurencyjny, to zmniejszana jest głębokość rekurencji w mutexie. Jedynie gdy głębokość jest zero mutex zostaje faktycznie zwolniony. Funkcja zwraca błąd EINVAL jeśli mutex nie został wcześniej poprawnie zainicjalizowany. Usuwanie Funkcja pthread_mutex_destroy niszczy obiekt mutexu i zwalnia zasoby z nim związane. Mutex musi być wolny wpp zwracana jest wartość EBUSY. Żadna z funkcji obsługujących mutexy nie jest cancellation point i żadna nie powinna być wykonywana z funkcji obsługi sygnału (nie jest async-signal safe). Przykład: Zmienna dzielona x może być w następujący sposób chroniona przez mutex: int x; pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER; Wszystkie operacje na zmiennej x powinny być otoczone wywołaniami pthread_mutex_lock i pthread_mutex_unlock: pthread_mutex_lock(&mut); /* operacje na x */

pthread_mutex_unlock(&mut); Rodzaje muteksów szybkie muteksy może dojść do zakleszczenia jeśli np z jednego wątku będziemy chcieli wywołać zagnieżdżone funkcje tworzenia mutexu (domyślny rodzaj w linuxie) rekurencyjne muteksy system wyłapuje rekurencyjne blokowania i pozwala się wątkowi wykonywać dalej (jest wbudowany licznik wywołań, żeby zwolnić mutex pracy na danym mutex'ie na najwyższym poziomie) sprawdzające błędy drugie wywołanie zwróci kod EDEADLK Drugi i trzeci rodzaj muteksow dostępny jest tylko na linuxie i zaleca się używanie ich tylko przy testowaniu i debuggowaniu. Przykład Aby utworzyć takie muteksy należy użyć poniższych funkcji pthread_mutex_attr_ tatr; pthread_mutex_t muter; pthread_mutex_attr_int(&attr); pthread_mutexattr_setkind_np (&attr, PTHREAD_MUTEX_ERRORCHECK_NP / PTHREAD_MUTEX_RECURSIVE_NP); pthread_mutex_init(&mutex,&attr); pthread_mutex_attr_destroy(&attr); #include <malloc.h> #include <pthread.h> struct job /* Link field for linked list. */ struct job* next; /* Other fields describing work to be done... */ ; /* A linked list of pending jobs. */ struct job* job_queue; /* A mutex protecting job_queue. */ pthread_mutex_t job_queue_mutex = /* Process queued jobs until the queue is empty. */ void* thread_function (void* arg) while (1) struct job* next_job; /* Lock the mutex on the job queue. */ pthread_mutex_lock (&job_queue_mutex); /* Now it s safe to check if the queue is empty. */ if (job_queue == NULL) next_job = NULL; else /* Get the next available job. */ next_job = job_queue; /* Remove this job from the list. */ job_queue = job_queue->next; /* Unlock the mutex on the job queue because we re done with the queue for now. */

pthread_mutex_unlock (&job_queue_mutex); /* Was the queue empty? If so, end the thread. */ if (next_job == NULL) break; /* Carry out the work. */ process_job (next_job); /* Clean up. */ free (next_job); return NULL; Semafory dla wątków Tworzenie semafora int sem_init (sem_t *sem, int pshare, unsigned, int value); Argumenty: pshare NULL ( linux nie obsługuje współdzielenia między procesami) value wartość początkowa Jeśli zmienna sem nie jest już potrzebna to można od razu użyć funkcji destroy_sem; Licznik semafora Każdy semafor posiada wartość licznika umożliwiająca wykonanie dwóch operacji: wait (sem_wait) zmniejsza wartość semafora o 1. Jeżeli wartość była 0 to wartość jest zmniejszana o 1, a wątki są blokowane do czasu aż licznik będzie równy 0 post (sem_post) zwiększa wartość semafora o 1. Istnieje jeszcze nie blokująca wersja funkcji (analogiczna do wait) sem_trywait podobna do pthread_mutex_trylock. Jeżeli semafor jest zablokowany to zwraca EAGAIN. Można pobrać (w linux ie) wartość licznika semafora za pomocą funkcji sem_getvalue #include <malloc.h> #include <pthread.h> #include <semaphore.h> struct job /* Link field for linked list. */ struct job* next; /* Other fields describing work to be done... */ ; /* A linked list of pending jobs. */ struct job* job_queue; /* A mutex protecting job_queue. */

pthread_mutex_t job_queue_mutex = PTHREAD_MUTEX_INITIALIZER; /* A semaphore counting the number of jobs in the queue. */ sem_t job_queue_count; /* Perform one-time initialization of the job queue. */ void initialize_job_queue () /* The queue is initially empty. */ job_queue = NULL; /* Initialize the semaphore which counts jobs in the queue. Its initial value should be zero. */ sem_init (&job_queue_count, 0, 0); /* Process queued jobs until the queue is empty. */ void* thread_function (void* arg) while (1) struct job* next_job; /* Wait on the job queue semaphore. If its value is positive, indicating that the queue is not empty, decrement the count by 1. If the queue is empty, block until a new job is enqueued. */ sem_wait (&job_queue_count); /* Lock the mutex on the job queue. */ pthread_mutex_lock (&job_queue_mutex); /* Because of the semaphore, we know the queue is not empty. Get the next available job. */ next_job = job_queue; /* Remove this job from the list. */ job_queue = job_queue->next; /* Unlock the mutex on the job queue because we re done with the queue for now. */ pthread_mutex_unlock (&job_queue_mutex); /* Carry out the work. */ process_job (next_job); /* Clean up. */ free (next_job); return NULL; /* Add a new job to the front of the job queue. */ void enqueue_job (/* Pass job-specific data here... */) struct job* new_job; /* Allocate a new job object. */ new_job = (struct job*) malloc (sizeof (struct job)); /* Set the other fields of the job struct here... */ /* Lock the mutex on the job queue before accessing it. */ pthread_mutex_lock (&job_queue_mutex); /* Place the new job at the head of the queue. */ new_job->next = job_queue; job_queue = new_job; /* Post to the semaphore to indicate that another job is available. If threads are blocked, waiting on the semaphore, one will become unblocked so it can process the job. */ sem_post (&job_queue_count); /* Unlock the job queue mutex. */ pthread_mutex_unlock (&job_queue_mutex);

Zmienne warunków Opis Zmienna warunku umożliwia zaimplementowanie warunku pod którym zmienna się wykonuje i przeciwnie warunku pod którym wątek jest zablokowany. Linux gwarantuje odblokowanie czekających wątków gdy nastąpi zmiana zmiennej warunku. Idea jest podobna do semaforów. Jeżeli watek A czeka znamienna warunku to jest zablokowany dopóki wątek B nie zasygnalizuje tej zmiennej. W przeciwieństwie do semafora zmienna warunku nie ma pamięci- jeśli wątek b zasygnalizuje zmienna B, ale w tym czasie wątek A nie będzie czekał na warunku to sygnał zostanie utracony. Sprawdzanie zmiennej warunku 1. Blokowanie muteksu i pobranie wartości flagi(zmiennej warunku) 2. Jeżeli flaga ustawiona to zwolnij muteks i kontynuuj działanie 3. Jeżeli flaga nie jest ustawiona to atomowo zwalnia muteks i czeka na zmianę warunku Ustawianie zmiennej warunku 1. Blokowanie muteksu (tego samego co przy sprawdzaniu) 2. Działania zmieniające zmienną warunku (np. flagę) 3. Sygnalizacja lub rozesłanie zmiennej warunku 4. Odblokowanie muteksu Funkcje operujące na zmiennych warunku (pthread_cond_t) int pthread_cond_int (pthread_cond_t *cond,const pthread_condattr_t *attr) inicjuje zmienna warunku (drugi argument ignorowany) int pthread_con_signal(pthread_cond_t *cond) sygnalizuje zmienna warunkuzostaje odblokowany jeden wątek int pthread_con_broadcast(pthread_cond_t *cond) rozgłasza zmienna warunkuzostają odblokowane wszystkie wątki int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *muter) blokuje wywołujący ją wątek do czasu zasygnalizowanie zmiennej warunku. #include <pthread.h> int thread_flag; pthread_mutex_t thread_flag_mutex; void initialize_flag () pthread_mutex_init (&thread_flag_mutex, NULL); thread_flag = 0; /* Calls do_work repeatedly while the thread flag is set; otherwise spins. */ void* thread_function (void* thread_arg) while (1)

int flag_is_set; /* Protect the flag with a mutex lock. */ pthread_mutex_lock (&thread_flag_mutex); flag_is_set = thread_flag; pthread_mutex_unlock (&thread_flag_mutex); if (flag_is_set) do_work (); /* Else don t do anything. Just loop again. */ return NULL; /* Sets the value of the thread flag to FLAG_VALUE. */ void set_thread_flag (int flag_value) /* Protect the flag with a mutex lock. */ pthread_mutex_lock (&thread_flag_mutex); thread_flag = flag_value; pthread_mutex_unlock (&thread_flag_mutex); Obsługa sygnałów System linux implementuje wątki jako osobne procesy (ale na zasadach wątku), ale w normie POSIX u nie jest założone który wątek otrzymuje sygnał. Linux zakłada jednak że zawsze otrzymuje sygnał najstarszy proces. Nie należy jednak zakładać takiego zachowania dla przenoszonych na inne platformy programów. Do wysyłania sygnałów pomiędzy wątkami należy używać funkcji pthread_kill Debugging wątków Z powodu istnienia nierównoważnych implementacji wątków, nie powstał dotąd żaden program umożliwiający bezpośrednie obserwowanie działania poszczególnych wątków. Programiści, aby zbadać działanie swojego programu, zmuszeni są więc do stosowania różnych sztuczek: metoda printf(): najstarszy chyba znany sposób sprawdzania poprawności działania programu :-) metoda attach z gdb: wykorzystując fakt, że wątki nie dzielą jeszcze PID z rodzicem, można "podłączyć" debugger gdb do wątku i obserwować działanie tego wątku obróbka zrzutu pamięci (plik core): ponieważ jądro nie tworzy zrzutu dla procesu, który współdzielił swoje części, jedyne co możemy odczytać z pliku core, to obraz ostatniego wątku - najczęściej jednak nie tego, który spowodował zakończenie programu Pytania 1) Chcac uzywac watkow w programie musisz dolaczyc plik naglowkowy biblioteke a) pthread (*) b) unixthread

c) threads d) nie ma mozliwosci uzywaniea watkow w unixie bo nie definuje ich norma POSIX 2) Watki jednego procesu NIE dziela a) sekcji danych (zmienne gloobalne itd) b) sekcji kodu programu- kod jest kopiowany dla kazdego watku osobno c) stosu - gdyby dzielily nie moglyby wywolywac roznych funkcji w roznych watkach (*) d) watki nic nie dziela miedzy soba 3) MUTEX to a) zwykla zmienna tylko nazwana inaczej ze wzgledu na zastosowanie b) rodzaj wzajaemnego wykluczania (MUTual EXclusion) uzywany do synchronizacji watkow (*) c) rodzaj sygnalu systemowego uzywanego do synchronizacji miedzy watkami (MUlti Thread EXamination) d) mechanizm systemowy uzywany w programach korzystajacych z watkow i wypisujacych na ekran (MultiUser TEXting) 4) Mechanizmy ktore NIE mechanizmem synchronizacji watkow to: a) semafory b) zmienne warunku c) MUREX d) pliki tymczasowe (*) 5) identyfikator watku jest typu a) zawsze typu int b) pthread_t (*) c) thread d) pid_t - taki sam typ jak dla procesu ale inna wartosc Spis Treści Wstęp... 1 Wątki poziomu użytkownika... 1

Wątki poziomu jądra... 2 Kombinacje obu rozwiązań... 2 Wątki vs. Procesy... 2 Wątki współdzielą (w przeciwieństwie do procesów)... 2 Wątki nie współdzielą... 2 Biblioteka: libpthread... 2 Tworzenie wątku... 3 Deklaracja... 3 Opis... 3 Oczekiwanie na zakończenie innego wątku... 4 Deklaracja... 4 Opis... 4 Uzyskanie własnego identyfikatora... 5 Deklaracja... 5 Opis... 5 Porównanie dwóch identyfikatorów wątków... 5 Ustawianie atrybutów przy tworzeniu wątków... 5 Zakończenie działania wątku... 6 Deklaracja... 6 Opis... 6 Anulowanie wątku... 6 Deklaracja... 6 Opis... 6 Tworzenie punktów anulowania... 6 Tworzenie sekcji krytycznych... 6 Dane własne wątku... 7 Mutex (MUTual Exclusion lock)... 7 Deklaracja... 7 Opis... 8 Inicjacja... 8 Blokowanie/ odblowokanie... 8 Usuwanie... 8 Przykład:... 8 Rodzaje muteksów... 9 Przykład... 9 Semafory dla wątków... 10 Tworzenie semafora... 10

Licznik semafora... 10 Zmienne warunków... 12 Opis... 12 Sprawdzanie zmiennej warunku... 12 Ustawianie zmiennej warunku... 12 Funkcje operujące na zmiennych warunku (pthread_cond_t)... 12 Obsługa sygnałów... 13 Debugging wątków... 13 Pytania... 13 Spis Treści... 14