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

Podobne dokumenty
Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.4

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

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

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

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

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

Problemy współbieżności

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO. Mariusz RUDNICKI: pok. 753 tel.:

Wielowątkowy serwer TCP

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

Systemy operacyjne III

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

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

synchronizacji procesów

synchronizacji procesów

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

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

Mariusz Rudnicki PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO CZ.2

Synchronizacja procesów i wątków

Co to jest sterta? Sterta (ang. heap) to obszar pamięci udostępniany przez system operacyjny wszystkim działającym programom (procesom).

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

PROGRAMOWANIE SYSTEMÓW CZASU RZECZYWISTEGO

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

Wielozadaniowość w systemie Microsoft Windows

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

1. Procesy i współbieżność

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

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

Wskaźniki w C. Anna Gogolińska

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

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

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

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

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

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

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

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

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

SYSTEMY CZASU RZECZYWISTEGO - VxWorks

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

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

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

Programowanie Współbieżne

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

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

Programowanie w języku C++

Tablice, funkcje - wprowadzenie

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

Modele programowania równoległego. Pamięć współdzielona Rafał Walkowiak dla III roku Informatyki PP

Futex (Fast Userspace Mutex) Łukasz Białek

procesów Współbieżność i synchronizacja procesów Wykład prowadzą: Jerzy Brzeziński Dariusz Wawrzyniak

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

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

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

9. Problem wzajemnego wykluczania i sekcji krytycznej

Wskaźniki. Przemysław Gawroński D-10, p marca Wykład 2. (Wykład 2) Wskaźniki 8 marca / 17

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

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

Lab 9 Podstawy Programowania

Argumenty wywołania programu, operacje na plikach

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

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

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

procesy odrębne dzielone

Prezentacja systemu RTLinux

Wskaźniki. Programowanie Proceduralne 1

Wykład 5: Klasy cz. 3

PROE wykład 2 operacje na wskaźnikach. dr inż. Jacek Naruniec

Klasy Obiekty Dziedziczenie i zaawansowane cechy Objective-C

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

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

Projektowanie oprogramowania systemów WĄTKI I SYNCHRONIZACJA

1. Wartość, jaką odczytuje się z obszaru przydzielonego obiektowi to: a) I - wartość b) definicja obiektu c) typ oboektu d) p - wartość

Semafor nie jest mechanizmem strukturalnym. Aplikacje pisane z użyciem semaforów są podatne na błędy. Np. brak operacji sem_post blokuje aplikację.

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

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

Zaawansowane programowanie w języku C++ Zarządzanie pamięcią w C++

Języki programowania C i C++ Wykład: Typy zmiennych c.d. Operatory Funkcje. dr Artur Bartoszewski - Języki C i C++, sem.

4. Procesy pojęcia podstawowe

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

Materiał Typy zmiennych Instrukcje warunkowe Pętle Tablice statyczne Wskaźniki Tablice dynamiczne Referencje Funkcje

1. Tworzenie nowego projektu.

Obsługa wyjątków. Język C++ WW12

Podstawy programowania komputerów

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

Widoczność zmiennych Czy wartości każdej zmiennej można zmieniać w dowolnym miejscu kodu? Czy można zadeklarować dwie zmienne o takich samych nazwach?

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

Globalne / Lokalne. Wykład 15. Podstawy programowania (język C) Zmienne globalne / lokalne (1) Zmienne globalne / lokalne (2)

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 6

Synchronizacja procesów

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

5. Model komunikujących się procesów, komunikaty

Jedrzej Ułasiewicz Komputerowe systemy sterowania 1

/* dołączenie pliku nagłówkowego zawierającego deklaracje symboli dla wykorzystywanego mikrokontrolera */ #include <aduc834.h>

Systemy operacyjne. Zajęcia 11. Monitory

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

Wskaźniki. Informatyka

Laboratorium 6: Dynamiczny przydział pamięci. dr inż. Arkadiusz Chrobot dr inż. Grzegorz Łukawski

Wykład 8: klasy cz. 4

Transkrypt:

Mariusz Rudnicki mariusz.rudnicki@eti.pg.gda.pl PROGRAMOWANIE WSPÓŁBIEŻNE I SYSTEMY CZASU RZECZYWISTEGO CZ.4

Synchronizacja wątków Omawiane zagadnienia Czym jest synchronizacja wątków? W jakim celu stosuje się mechanizmy synchronizacji? Metody synchronizacji w RT OS: QNX Neutrino, RT Linux, Windows CE. Niebezpieczeństwa związane z synchronizacją. 2/62

Synchronizacja wątków Wątki dostarczają nowych rozwiązań, ale także nowych problemów: Wspólne obszary pamięci: wielokrotne zapisy mogą zamazać oczekiwaną wartość, wątek czytający dane nie wie, kiedy dane są stabilne. Podobne problemy występują z innymi współdzielonymi zasobami: kod programu; zasoby sprzętowe. 3/62

Synchronizacja wątków Problem stanowi synchronizacja pracy wątków. Rozwiązaniem problemu synchronizacji są metody przedstawione w dalszej części prezentacji. Szerzej omawiane: mutexy, zmienne warunkowe, semafory, bariery, joining, operacje atomowe. 4/62

Synchronizacja wątków Pozostałe metody stosowane w QNX Neutrino: rwlocks: pozwala na wielokrotne czytanie bez możliwości zapisu, tylko jeden zapis bez możliwości czytania, jednokrotna inicjalizacja: Pierwsze wywołanie pthread_once z danym argumentem once_control powoduje wykonanie kodu bezargumentowej procedury init_routine i zmienia wartość zmiennej once_control żeby zaznaczyć, ze inicjalizacja była wykonana. Kolejne wywołania pthread_once z tym samym argumentem once_control będą pustymi wywołaniami; 5/62

Synchronizacja wątków Pozostałe metody stosowane w QNX Neutrino: dane własne wątku: Zmienne są powielane dla każdego wątku i każdy może modyfikować swoją kopię zmiennej bez wpływu na inne wątki. 6/62

Synchronizacja wątków słowo kluczowe volatile: zmienne współdzielone: powinny być zadeklarowane jako volatile; volatile unsigned flags;... atomic_clr (&flags, A_FLAG); volatile jest słowem kluczowym ANSI C informującym kompilator żeby nie optymalizował tej zmiennej. przykład: Kompilator może optymalizować kod umieszczając wartości w rejestrze i odnosząc się do rejestru zamiast sięgania do pamięci. Tymczasem inny wątek uzyskiwałby dostęp do pamięci! 7/62

Synchronizacja Mutual Exclusion Mutual exclusion zgodny z POSIX. Wzajemne wykluczenie ang. Mutual exclusion oznacza, że tylko jeden wątek: ma dostęp do krytycznej sekcji kodu w danym momencie; ma dostęp do szczególnego fragmentu danych w danej chwili. 8/62

Synchronizacja Mutual Exclusion Wzajemne wykluczanie projektuje się tak, by zsynchronizować wątki w taki sposób, aby każdy przetwarzał dane i wykonywał sekcję krytyczną w sposób nie kolidujący z wykonaniem sekcji krytycznej innych wątków. Do realizacji tego zagadnienia, należy do funkcji każdego wątku dodać dodatkowe instrukcje poprzedzające i następujące po sekcji krytycznej. 9/62

Synchronizacja Mutual Exclusion API dostarcza następujących wywołań: administracyjnych: pthread_mutex_init (pthread_mutex_t *, pthread_mutexattr_t *); pthread_mutex_destroy (pthread_mutex_t *); użytkowych: pthread_mutex_lock (pthread_mutex_t *); pthread_mutex_trylock (pthread_mutex_t *); pthread_mutex_unlock (pthread_mutex_t *); 10/62

Synchronizacja Mutual Exclusion Przykład: pthread_mutex_t mymutex; init () { domyślne... atrybuty // utworzenie mutexu pthread_mutex_init (&mymutex, NULL);... } thread_func () {... // otrzymaj mutex, czekaj w razie potrzeby pthread_mutex_lock (&mymutex); // operowanie na krytycznych danych // koniec krytycznej sekcji, zwolnij mutex pthread_mutex_unlock (&mymutex);... } cleanup () { pthread_mutex_destroy (&mymutex); } 11/62

Synchronizacja Mutual Exclusion Rozważmy sytuację: freelist Pewna liczba wątków żąda alokacji pamięci wywołując funkcję malloc(). Wewnętrznie, malloc() przechowuje listę wolnych bloków pamięci, które są dostępne do alokacji. Wszystkie wątki w procesie używają tej samej listy. memoryarea 1 memoryarea 2 memoryarea 3 memoryarea 4 NULL 12/62

Synchronizacja Mutual Exclusion W uproszczeniu źródło malloc() wygląda następująco: void * malloc (int nbytes) { while (freelist && freelist -> size!= nbytes) { freelist = freelist -> next; } if (freelist) { // mark block as used, and return block address to caller return (freelist -> memory_block); } } 13/62

Synchronizacja Mutual Exclusion Rozważmy pewną liczbę wątków, które używają malloc(): thread1 () { char *data; } data = malloc (64); thread2 () { char *other_data; } other_data = malloc (64); 14/62

Synchronizacja Mutual Exclusion 15/62

Synchronizacja Mutual Exclusion Problem stanowi rywalizacja wątków o wspólne zasoby, wiele wątków może wchodzić sobie w drogę! Rozwiązaniem problemu jest wyłączny dostęp do struktury danych! Rozwiązanie z użyciem mutexu! 16/62

Synchronizacja Mutual Exclusion Poprawiony kod funkcji malloc(): pthread_mutex_t malloc_mutex; void * malloc (int nbytes) { pthread_mutex_lock (&malloc_mutex); while (freelist && freelist -> size!= nbytes) { freelist = freelist -> next; } if (freelist) { // mark block as used, and return block block = freelist -> memory_block; pthread_mutex_unlock (&malloc_mutex); return (block); } pthread_mutex_unlock (&malloc_mutex); } 17/62

Synchronizacja Mutual Exclusion Aby zainicjować mutex należy wykonać następujący fragment kodu: pthread_mutex_init (&malloc_mutex, NULL); Jeżeli zakończy się powodzeniem gwarantuje, że wszystkie odpowiednie zasoby zostały zaalokowane dla mutexa. 18/62

Synchronizacja Mutual Exclusion Prosta metoda inicjalizacji mutexu: // statyczna inicjalizacja Mutexu pthread_mutex_t malloc_mutex = PTHREAD_MUTEX_INITIALIZER; void * malloc (int nbytes) {... // MUTEX będzie zainicjowany po pierwszym //użyciu pthread_mutex_lock (&malloc_mutex);... 19/62

Synchronizacja Mutual Exclusion Domyślnie mutexy nie mogą być współdzielone między procesami; aby współdzielić mutexa, ustawiamy dla niego flagę PTHREAD_PROCESS_SHARED; mutex powinien być zdefiniowany w pamięci współdzielonej; np.: pthread_mutexattr_t mutex_attr; pthread_mutex_t *mutex; pthread_mutexattr_init( &mutex_attr ); pthread_mutexattr_setpshared( &mutex_attr, PTHREAD_PROCESS_SHARED); mutex = (pthread_mutex_t *)shmem_ptr; pthread_mutex_init( mutex, &mutex_attr ); 20/62

Synchronizacja Mutual Exclusion Pamiętaj! Żaden wątek nie może wykonywać swej sekcji krytycznej nieskończenie długo, nie może się w niej zapętlić lub zakończyć w wyniku jakiegoś błędu; W sekcji krytycznej wątek przebywa jak najkrócej i nie może zakończyć się błędem. 21/62

Synchronizacja Condition variables Rozważmy prosty przypadek, gdzie: musimy zablokować wątek, oczekując aż inny wątek zmieni pewną zmienną: int state; thread_1 (){ } while (1) { } // wait until state changes, // then, perform some work Zmienne warunkowe dostarczają mechanizm oczekiwania na taką zmianę. 22/62

Synchronizacja Condition variables Wywołania: pthread_cond_init (pthread_cond_t *, pthread_condattr_t *); pthread_cond_wait (pthread_cond_t *, pthread_mutex_t *); pthread_cond_signal (pthread_cond_t *); pthread_cond_broadcast (pthread_cond_t *); 23/62

Synchronizacja Condition variables Wywołania: wątek dostarczający dane dostaje pewną ich ilość, np. od procesu klienta i dodaje je do kolejki; powiadamia wątek obsługi sprzętu o nowych danych; wątek obsługi sprzętu budzi się, pobiera dane z kolejki i przesyła je do sprzętu. 24/62

Synchronizacja Condition variables Żeby to wykonać potrzebujemy dwóch rzeczy: mutexa aby zapewnić pojedynczy dostęp do kolejki danych, mechanizmu dla wątku dostarczającego dane by poinformował wątek obsługi sprzętu o nowych danych i obudził go. 25/62

Synchronizacja Condition variables Kod wątku obsługi sprzętu: while (1) { pthread_mutex_lock (&mutex); // wyłączność dostępu if (!data_ready) pthread_cond_wait (&cond, &mutex); // oczekiwanie na dane } /* pobranie danych z kolejki */ while ((data = get_data_and_remove_from_queue ())!= NULL) { pthread_mutex_unlock (&mutex); write_to_hardware (data); // przekaż do urządzenia free (data); // zwolnij zasoby pthread_mutex_lock (&mutex); } data_ready = 0; // ponowne ustawienie flagi pthread_mutex_unlock (&mutex); 26/62

Synchronizacja Condition variables Kod wątku dostarczającego dane: pthread_mutex_lock (&mutex); // wyłączność dostępu add_to_queue (buf); data_ready = 1; // ustawienie flagi pthread_cond_signal (&cond); // powiadomienie oczekującego pthread_mutex_unlock (&mutex); // zwolnienie dostępu 27/62

Synchronizacja Condition variables Przyjrzyjmy się bliżej funkcji wait : pthread_cond_wait (&condvar, &mutex); Zawiesza sekcję krytyczną ustanowioną przez mutex podany jako drugi parametr (dlatego przed wywołaniem tej funkcji wątek powinien ustawić sekcję krytyczną za pomocą mutexa), następnie usypia aktualny wątek. Podczas sukcesu ponownie ustawia sekcję krytyczną i zwraca 0. 28/62

Synchronizacja Condition variables Dlaczego wykonujemy to sprawdzenie? while (1) { } 2 1 pthread_mutex_lock (&mutex); if (data_ready == 0)... jeżeli sygnalizujemy zmianę stanu zmiennej warunkowej i żaden wątek nie czeka na taki sygnał, jest on tracony; sygnał powinien być wysłany pomiędzy 1 i 2 ; // wyłączny dostęp pthread_cond_wait (&cond, &mutex); // oczekujemy data_ready = 0; pthread_mutex_unlock (&mutex); // ponownie ustawiamy flagę proces sygnalizujący ustawia też flagę (data_ready = 1) 29/62

Synchronizacja Condition variables Sygnalizacja vs broadcast: wątki 1, 2 i 3 (wszystkie o takim samym priorytecie) czekają na zmianę używając pthread_cond_wait(), Wątek 4 dokonuje zmiany i sygnalizuje za pomocą pthread_cond_signal(), Najdłużej czekający wątek (powiedzmy 2 ) jest informowany o zmianie i próbuje zablokować mutex ( automatycznie przez pthread_cond_wait()), Wątek 2 sprawdza warunek, przeprowadza działania lub wraca do uśpienia 30/62

Synchronizacja Condition variables Co się dzieje z wątkami 1 i 3? nigdy nie zauważą zmiany! Jeżeli zmienimy przykład: wykorzystamy pthread_cond_broadcast() zamiast pthread_cond_signal(), wtedy wszystkie trzy wątki otrzymają powiadomienie wszystkie wątki zmienią stan na READY, jednak tylko jeden z nich może zablokować mutex jednocześnie - pozostałe kolejno. 31/62

Synchronizacja Condition variables Jaki sposób sygnalizowani wybrać? wybieramy sygnał, jeżeli: mamy jeden oczekujący wątek; potrzebujemy uruchomić tylko jeden wątek do przetwarzania i nie musimy informować pozostałych; używamy broadcast, jeżeli mamy wiele wątków i: wszystkie muszą przeprowadzić działania po dokonanej zmianie; lub nie wszystkie muszą zareagować na zmianę, ale nie wiemy, który z nich obudzić. 32/62

Synchronizacja Condition variables Domyślnie, zmienne warunkowe nie mogą być współdzielone z innymi procesami: aby współdzielić zmienne warunkowe, ustawiamy odpowiednią flagę PTHREAD_PROCESS_SHARED: zmienna warunkowa powinna być w pamięci współdzielonej: pthread_condattr_t cond_attr; pthread_cond_t *cond; pthread_condattr_init( &cond_attr ); pthread_condattr_setpshared( &cond_attr, PTHREAD_PROCESS_SHARED); cond = (pthread_cond_t *)shmem_ptr; pthread_cond_init(cond, &cond_attr ); 33/62

Synchronizacja Condition variables Przykład producenta/konsumenta: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; volatile int state = 0; volatile int product = 0; void *consume (void *arg) { } while (1) { pthread_mutex_lock (&mutex); while (state == 0) { } pthread_cond_wait (&cond, &mutex); printf ( Consumed %d\n, product); state = 0; pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); do_consumer_work (); } return (0); 34/62

Synchronizacja Condition variables Przykład producenta/konsumenta: pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t cond = PTHREAD_COND_INITIALIZER; volatile int state = 0; volatile int product = 0; void *consume (void *arg) { } while (1) { pthread_mutex_lock (&mutex); while (state == 0) { } pthread_cond_wait (&cond, &mutex); printf ( Consumed %d\n, product); state = 0; pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); do_consumer_work (); } return (0); 35/62

Synchronizacja Condition variables void *produce (void *arg) { } while (1) { } pthread_mutex_lock (&mutex); while (state == 1) { pthread_cond_wait (&cond, &mutex); } printf ( Produced %d\n, product++); state = 1; pthread_cond_signal (&cond); pthread_mutex_unlock (&mutex); do_producer_work (); return (0); int main () { pthread_create (NULL, NULL, &produce, NULL); consume (NULL); return (EXIT_SUCCESS); } 36/62

Synchronizacja Semaphores Semafor - jest obiektem abstrakcyjnym służącym do kontrolowania dostępu do ograniczonego zasobu. Semafory są szczególnie przydatne w środowisku gdzie wiele procesów lub wątków komunikuje się przez wspólną pamięć. 37/62

Synchronizacja Semaphores W standardzie POSIX wyróżnione są dwa typy semaforów: Semafory nienazwane identyfikowane po adresie semafora. Stąd nazwa semafor nienazwany. Semafory nazwane - identyfikowane są w procesach poprzez ich nazwę. Na semaforze nazwanym operuje się tak samo jak na semaforze nienazwanym z wyjątkiem funkcji otwarcia i zamknięcia semafora. 38/62

Synchronizacja Semaphores Wykorzystanie semaforów dla sterowania dostępem administracja: sem_init (sem_t *semaphore, int pshared, unsigned int val); sem_destroy (sem_t *semaphore); unnamed semaphores sem_t *sem_open (char *name, int oflag, [int sharing, unsigned int val]); sem_close (sem_t *semaphore); named semaphores sem_unlink (char *name); użycie: sem_post (sem_t *semaphore); sem_trywait (sem_t *semaphore); sem_wait (sem_t *semaphore); sem_getvalue (sem_t *semaphore, int *value); 39/62

Synchronizacja Semaphores Nienazwane vs nazwane semafory: z nienazwanymi, wywołania sem_post() i sem_wait() powodują bezpośrednie wywołanie jądra związane z semaforem, z nazwanymi, wywołania sem_post() i sem_wait() wysyłają komunikaty do mqueue a stąd generowane są wywołania jądra, semafory nienazwane są szybsze niż nazwane, semafory nazwane wykorzystywane są do komunikacji pomiędzy procesami. 40/62

QNX Neutrino Inne mechanizmy synchronizacji BARRIERS: Bariera jest mechanizmem synchronizującym, który pozwala gromadzić współpracujące ze sobą wątki i zmusić je do oczekiwania w pewnym wyspecyfikowanym punkcie dopóki wszystkie nie wykonają określonych czynności. W przeciwieństwie do mechanizmu joining w przypadku barier oczekujemy, że wątki spotkają się w określonym przez programistę punkcie. Kiedy określona liczba wątków dotrze do bariery, wszystkie zostaną odblokowane i mogą kontynuować pracę. 41/62

QNX Neutrino Inne mechanizmy synchronizacji BARRIERS: Sposób postępowania: Tworzymy obiekt bariery: pthread_barrier_init(); int pthread_barrier_init (pthread_barrier_t *barrier, const pthread_barrierattr_t *attr, unsigned int count); Argument count zawiera liczbę wątków, które muszą osiągnąć barierę; Po wywołaniu pthread_barrier_wait() wątek zostaje zablokowany dopóki określona, w funkcji pthread_barrier_init( ), liczba wątków nie wywoła funkcji pthread_barrier_wait(). 42/62

QNX Neutrino Inne mechanizmy synchronizacji BARRIERS: Kiedy właściwa liczba wątków wywoła funkcję pthread_barrier_wait(), inaczej mówiąc osiągnie barierę, wszystkie zostają kolejno odblokowane. Liczba wątków, które muszą dotrzeć do bariery musi uwzględniać również wątek główny, w którym tworzony i inicjowany jest obiekt bariery. 43/62

QNX Neutrino Inne mechanizmy synchronizacji BARRIERS: Funkcje administrujące: pthread_barrier_init(); pthread_barrierattr_init(); pthread_barrierattr_destroy(); pthread_barrierattr_getpshared(); pthread_barrierattr_setpshared(); pthread_barrier_destroy(). Funkcje użytkowe: pthread_barrier_wait(). 44/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: Najprostsza metoda synchronizacji oczekiwanie na zakończenie wątku. Rozważmy przykładowy kod: int num_lines_per_cpu, num_cpus; int main (int argc, char **argv){ int cpu; pthread_t *thread_ids;... // perform initializations thread_ids = malloc (sizeof (pthread_t) * num_cpus); 45/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: num_lines_per_cpu = num_x_lines / num_cpus; for (cpu = 0; cpu < num_cpus; cpu++) { pthread_create (&thread_ids [cpu], NULL, do_one_batch, (void *) cpu); } // synchronize to termination of all threads for (cpu = 0; cpu < num_cpus; cpu++) { pthread_join (thread_ids [cpu], NULL); } }... // display results 46/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: Funkcja pthread_create() w pierwszym parametrze zwraca adres do zmiennej typu pthread_t. Jest to miejsce, w którym przechowywany jest identyfikator nowo utworzonego wątku. W pierwszej pętli for( ) uruchamiamy num_cpus wątków, plus wątek główny procesu (z funkcją main()). Nie będziemy zajmować się wątkiem głównym, gdyż większość czasu spędzi on na oczekiwaniu. Oczekiwanie zakończy się poprzez wykonanie funkcji pthread_join() kolejno na każdym wątku, czyli sprawdzeniu czy zakończył swoją pracę. 47/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: W pierwszej kolejności oczekujemy na zakończenie wątku threads_ids[0]. Kiedy skończy on swoją pracę funkcja pthread_join() odblokuje wątek główny. W kolejnej iteracji w pętli for( ) będziemy czekać na zakończenie wątku thread_ids[1], itd. W tym miejscu pojawia się pytanie: Co się stanie gdy wątki zakończą swoją pracę w odwrotnej kolejności? 48/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: W obecnej sytuacji funkcja pthread_join() blokuje wątek główny na thread_ids[0]. Tymczasem wątek thread_ids[3] zakończył swoją pracę. Sytuacja ta nie ma jednak pływu na wątek główny, który nadal czeka na zakończenie wątku pierwszego. Zakończenie pracy przez wątek thread_ids[2], również nie ma wpływu na wątek główny. Podobnie dzieje się po zakończeniu pracy przez wątek thread_ids[1]. 49/62

QNX Neutrino Inne mechanizmy synchronizacji JOINING: W końcu, gdy wątek pierwszy zakończy swoją pracę wątek główny zostanie natychmiast odblokowany i będzie mógł wykonać następną iterację w pętli for( ). Kolejny wątek już nie pracuje, tak więc wątek główny nie będzie blokowany i będzie mógł wykonać kolejne operacje i zakończyć swoją pracę. 50/62

QNX Neutrino Inne mechanizmy synchronizacji ATOMIC OPERATION: Pewne praktyczne funkcje zdefiniowane w pliku nagłówkowym <atomic.h> pozwalają wykonywać operacje atomowe (operacje, które gwarantują niepodzielność i nieprzerwalność). Operacje te nie mogą zostać wydziedziczone przez inny wątek lub funkcję obsługi przerwania ISR. 51/62

QNX Neutrino Inne mechanizmy synchronizacji ATOMIC OPERATION: Używając tych funkcji łagodzimy potrzebę wyłączania i włączania przerwań na krótki czas wykonywania ściśle określonych operacji na zmiennych, takich jak: Dodawanie, odejmowanie; Ustawianie i czyszczenie bitów; Zmianę wartości bitów. 52/62

QNX Neutrino Inne mechanizmy synchronizacji ATOMIC OPERATION: Operacji atomowych możemy używać wszędzie. Szczególnie przydatne są w operacjach: Pomiędzy ISR i wątkiem; Pomiędzy dwoma wątkami (SMP lub pojedynczy procesor). Operacje atomowe ograniczają konieczność blokowania i ponownego włączania przerwań w systemach czasu rzeczywistego. 53/62

Synchronizacja RT Linux: mutexy, semafory zgodne z POSIX. 54/62

Synchronizacja Windows Embedded obiekty synchronizacji wątków: sekcje krytyczne, mutexy, zdarzenia, semafory, funkcje oczekujące. 55/62

Synchronizacja Windows Embedded sekcje krytyczne: chronią część kodu przed dostępem wielu wątków w jednym czasie, mogą być wykorzystywane w obrębie jednego procesu lub DLL, nie mogą być współdzielone przez inne procesy. void InitializeCriticalSection (LPCRITICAL_SECTION lpcriticalsection); void EnterCriticalSection (LPCRITICAL_SECTION lpcriticalsection); void LeaveCriticalSection (LPCRITICAL_SECTION lpcriticalsection); void DeleteCriticalSection (LPCRITICAL_SECTION lpcriticalsection); 56/62

Synchronizacja Windows Embedded mutexy: są obiektami, których stan sygnalizuje czy są używane przez wątek; koordynują wyłączny dostęp do współdzielonych zasobów; w danym czasie tylko jeden wątek może używać mutexa; mutex może być uważany za porzucony ang. abandoned w sytuacji gdy wątek zakończy pracę i nie zwolni mutexa; wątek oczekujący może uzyskać prawa do mutexa porzuconego; 57/62

Synchronizacja Windows Embedded mutexy: pojawienie się mutexa porzuconego sygnalizuje, że wystąpił błąd w systemie i stan wszelkich zasobów chronionych tym mutexem jest nieokreślony; jeżeli wątek uzna, że mutex nie jest porzucony i kontynuuje pracę to po zwolnieniu mutexa flaga porzucony zostaje skasowana i staje się on zwykłym mutexem; mutexy mogą być używane w synchronizacji pracy wielu procesów. 58/62

Synchronizacja Windows Embedded semafory: są obiektami: synchronizacji wątków wewnątrz jednego procesu, jak również synchronizacji międzyprocesowej; stan semafora jest sygnalizowany, kiedy jego licznik jest większy od zera i nie jest sygnalizowany, gdy licznik równy jest zero; semafor jest bramą użycia zasobu ograniczającą jego użycie poprzez zliczanie wątków przechodzących przez bramę; 59/62

Synchronizacja Windows Embedded semafory: za każdym razem gdy wątek oczekujący jest przepuszczany licznik semafora jest zmniejszana; aplikacja może stworzyć semafor z licznikiem = 0, powoduje to ustawienie semafora w stan niesygnalizowany i blokuje wszystkie wątki żądające dostępu do chronionego zasobu. 60/62

Synchronizacja Windows Embedded zdarzenia: zdarzenia są obiektami wykorzystywanymi do: powiadamiania wątku kiedy ma wykonać swoją pracę; wskazania, że dane zdarzenie miało miejsce; nazwane zdarzenia mogą być wykorzystywane do synchronizacji wątków różnych procesów. 61/62

Synchronizacja Windows Embedded funkcje oczekujące: są obiektami blokującymi lub odblokowującymi wątek, bazującymi na stanie obiektu: oczekiwanie na jeden obiekt WaitForSingleObject; oczekiwanie na wiele obiektów WaitForMultipleObjects; oczekiwanymi obiektami mogą być obiekty synchronizacji np. mutexy lub zdarzenia bądź też uchwyty do procesów lub wątków; funkcje oczekujące na wiele obiektów tworzą tablicę zawierającą jeden lub więcej obiektów synchronizacji np.: stan obiektu synchronizacji; upłynięcie określonego czasu. 62/62