WĄTKI I ICH SYNCHRONIZACJA

Download Report

Transcript WĄTKI I ICH SYNCHRONIZACJA

WĄTKI I ICH
SYNCHRONIZACJA
Natalia Wyczislok
Proces
• Procesem określamy zazwyczaj
wykonywany program w skład,
którego wchodzą: kod programu,
licznik rozkazów, stos procesu i
sekcja danych.
• System operacyjny jest
odpowiedzialny za tworzenie,
usuwanie, wstrzymywanie i
wznawianie procesów.
Wątek
• Jest to współbieżnie wykonujący się
fragment kodu danego procesu
• Wątek dzieli wraz z innymi równorzędnymi
wątkami kod programu, sekcję danych
oraz zasoby systemowe. Natomiast wątek
posiada własny licznik rozkazów, zbiór
rejestrów procesora i stos.
Hierarchia Wątków
• Każdy z procesów
składa się
przynajmniej z
jednego wątku
głównego którym
jest funkcja main.
• Dodatkowo wątek główny może tworzyć nowe wątki, a
te z kolei następne. W ten sposób tworzy się hierarchia
wątków.
Zalety Wątków
• możliwość łatwego dostępu do
wspólnych danych programu .
Komunikacja miedzy wątkami nie
musi odbywać się jak w przypadku
procesów za pomocą kosztownych
mechanizmów
• przełączanie procesora między
wątkami jest łatwiejsze niż między
procesami
Wady Wątków
• jeden błędnie działający wątek może zagrozić
działaniu całego procesu.
• wymagają synchronizacji, które zwiększają
rozmiaru programu, a także powodują
dodatkowe problemy na etapie tworzenia i
testowania programu
Tworzenie Wątku
Jeżeli utworzenie nowego wątku przebiegło pomyślnie
funkcja zwraca uchwyt wątku. W przypadku błędu zwrócona
zostaje wartość NULL.
HANDLE CreateThread(
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
Tworzenie Wątku
• lpThreadAttributes
wskaźnik na strukturę SECURITY_ATTRIBUTES
określającej atrybuty zabezpieczeń i dostępu do nowego
wątku. Wpisanie null spowoduje przyjęcie ustawień
domyślnych
• dwStackSize
początkowa (lub zalecana) wielkość stosu nowego
wątku, podanie wartości 0 spowoduje przyjęcie przez
system domyślnej wielkości stosu; w razie potrzeby
wielkość stosu jest automatycznie powiększana przez
system.
Tworzenie Wątku
• lpStartAddress – wskaźnik na funkcję wątku,
• lpParameter – wskaźnik na parametr przekazywany w
funkcji wątku,
• dwCreationFlags – znacznik kontrolujący proces
tworzenia wątku; wartość 0 spowoduje
natychmiastowe uruchomienie funkcji wątku; flaga
CREATE _ SUSPENDED spowoduje zawieszenie
wykonywania wątku do czasu wywołania funkcji
ResumeTheread;
Tworzenie Wątku
• lpThreadId – wskaźnik na zmienną zawierającą
unikatowy identyfikator wątku
DWORD WINAPI ThreadProc (LPVOID lpParameter)
• lpParameter – parametr przekazywany do funkcji
wątku
Kończenie Wątku
• Funkcja TerminateThread zakonczy watek w
dowolnym momencie, pamiec może nie zostac
zwolniona
• ExitThread – funkcja zalecana do konczenia
wątku, parametr okresla wartosc zwrocona
przez funkcje wątku
• Zakonczenie funkcji wątku czyli niejawne
wywolanie funkcji ExitThread
Sprzątanie po obiektach
• Zwalnianie zasobów po wątkach oraz
obiektach synchronizacji odbywa się za
pomocą funkcji CloseHandle();
Usypianie i przełączanie wątków
• VOID Sleep( DWORD dwMilliseconds) wątek dobrowolnie oddaje resztę
przydzielonego mu czasu cpu, nie jest
wykonywany przez czas podany w
argumencie.
• BOOL SwitchToThread() – jeżeli istnieje
wątek czekajacy na cpu to natychmiast
zostaje mu przekazany kwant czasu.
Kontekst wątku
• Struktura CONTEXT pozwala systemowi
pamiętać stan wątku (wartości rejestrów
CPU) i podjąć jego wykonywanie w
momencie, gdy znów uzyska on dostęp do
CPU
• Składowe struktury CONTEXT zależą od
architektury procesora
Sekcje struktury Context
• CONTEXT_CONTROL - obejmuje rejestry kontrolne
CPU, takie jak wskaźnik instrukcji,wskaźnik stosu, flagi i
adres powrotny funkcji.
• CONTEXT_INTEGER - obejmuje rejestry całkowite
CPU, czyli np. Eax, Ebx, Ecx, Edi, itp.
• CONEXT_FLOATING_POINT - obejmuje rejestry
zmiennoprzecinkowe.
• CONTEXT_SEGMENTS - obejmuje rejestry
segmentowe (tylko x86).
• CONTEXT_DEBUG_REGISTERS - obejmuje rejestry
debugowania (tylko x86).
• CONTEXT_EXTENDED_REGISTERS - rejestry
rozszerzone (tylko x86).
Rejestry wątku
• Aby dobrać się do wartości odpowiednich
rejestrów musimy na początku wrzucić do
zmiennej DWORD ContextFlags wartość
odpowiedniej sekcji (flage)
• Przed operacjami nad wartościami rejestru
trzeba pamiętać o zatrzymaniu wątku
funkcją: SuspendThread(hThread); a po
zakonczeniu wznowić
ResumeThread(hThread);
Rejestry wątku
• Odczyt:
GetThreadContext(HANDLE hThread,
LPCONTEXT lpContext );
• Modyfikacja
SetThreadContext( HANDLE
hThread, LPCONTEXT pContext)
Priorytety wątku
• Priorytety są złożeniem klasy priorytetu
procesu oraz względnych priorytetów
wątku.
• Wartość priorytetu to liczba od 0-31
• Planista systemu przydziela czas CPU
wątkowi o największym priorytecie.
Klasy priorytetu procesu
• Czasu rzeczywistego - wątki muszą natychmiast
reagować na zdarzenia. Wywłaszczają one nawet
składowe systemu operacyjnego
• Wysoki - wątki procesu muszą natychmiast reagować na
zdarzenia. Do tej klasy zalicza się Eksplorator Windows
czy Menadżer zadań.
• Powyżej normalnego
• Normalny - standard w systemie Windows. Jeżeli
tworzymy nowy proces lub wątek bez określonej
wcześniej klasy priorytetu to standardowo, klasa
priorytetu tych obiektów jest ustawiona na "normalny".
• Poniżej normalnego
• Niski - wątki takiego procesu wykonują się w momencie
bezczynności systemu. Przykładem jest wygaszasz
ekranu.
Wzgędne priorytety wątku
•
•
•
•
•
•
•
Krytyczny
Najlepszy
Powyżej normalnego
Normalny
Poniżej normalnego
Najgorszy
Niski.
Ustalanie priorytetu właściwego
Wzgledny
priorytet
wątku
Klasa priorytetu procesu
Niski
Pon.
Norm.
Normalny
Pow. Norm.
Wysoki
Czasu rzecz.
Krytyczny
15
15
15
15
15
31
Najlepszy
6
8
10
12
15
26
Pow.
norm.
5
7
9
11
14
25
Normalny
4
6
8
10
13
24
Pon. norm.
3
5
7
9
12
23
Najgorszy
2
4
6
8
11
22
Niski
1
1
1
1
1
16
Klasa priorytetu procesu
• REALTIME_PRIORITY_CLASS - czasu
rzeczywistego.
• HIGH_PRIORITY_CLASS - wysoki.
• ABOVE_NORMAL_PRIORITY_CLASS
powyżej normalnego.
• NORMAL_PRIORITY_CLASS - normalny.
• BELOW_NORMAL_PRIORITY_CLASS poniżej normalnego.
• IDLE_PRIORITY_CLASS - niski.
Klasa priorytetu procesu
• Odczyt:
DWORD GetPriorityClass( HANDLE
hProcess);
• Modyfikacja
BOOL SetPriorityClass( HANDLE
hProcess, DWORD fdwPriority);
Priorytet względny wątku
• THREAD_PRIORITY_TIME_CRITICAL krytyczny.
• THREAD_PRIORITY_HIGHEST - najlepszy.
• THREAD_PRIORITY_ABOVE_NORMAL powyżej normalnego.
• THREAD_PRIORITY_NORMAL - normalny.
• THREAD_PRIORITY_BELOW_NORMAL poniżej normalnego.
• THREAD_PRIORITY_LOWEST - najgorszy.
• THREAD_PRIORITY_IDLE - niski.
Priorytet względny wątku
• Odczyt:
GetThreadPriority( HANDLE
hThread)
• Modyfikacja
SetThreadPriority( HANDLE hThread,
int nPriority);
Synchronizacja
Metody synchronizacji
•
•
•
•
•
•
•
Operacje atomowe
Sekcje krytyczne
Funkcje oczekujące
Mutexy
Semafory
Zdarzenia
Zegary oczekujące
Operacje Atomowe
• Czyli operacje niepodzielne. Każda instrukcja w
języku wysokiego poziomu jest tłumaczona na
wiele instrukcji maszynowych.
Operacje atomowe zapewniają, że w czasie
wykonywania tych instrukcji wątek nie
zostanie wywłaszczony.
Operacje Atomowe
• InterlockedExchange, InterlockedExchange64 –
przypisanie zmiennej wybranej wartości
• InterlockedIncrement, InterlockedIncrement64 –
zwiększenie wartości zmiennej o 1
• InterlockedDecrement, InterlockedDecrement64 –
zmniejszenie wartości zmiennej o 1
• InterlockedExchangeAdd, InterlockedExchangeAdd64
– dodanie do zmiennej dowolnej wartości całkowitej
Operacje Atomowe
• InterlockedComapreExchange,
InterlockedExchangeAdd64 – porównanie
wartości dwóch zmiennych
• InterlockedExchangePointer – zamiana
wartości dwóch wskaźników,
• InterlockedCompareExchangePointer –
porównanie wartości dwóch wskaźników.
Sekcja Krytyczna
• Jest rozszerzeniem operacji atomowych.
Pozwala na objęcie nieprzerywalnością
wykonania wybranego fragmentu funkcji
wątku.
Sekcja Krytyczna
• Inicjalizacja:
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
• Zwolnienie zasobów:
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
Sekcja Krytyczna
• Wejscie do sekcji krytycznej:
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
• Wyjście z sekcji krytycznej:
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
Funkcje Oczekujace
• Zadaniem funkcji oczekujących jest
oczekiwanie na przejście obiektu w stan
sygnalizowany. Mechanizm ten obejmuje nie
tylko wątki ale także szereg innych rodzajów
obiektów, w tym opisywane dalej muteksy,
semafory, zdarzenia i zegary oczekujące.
Funkcje Oczekujące
• Oczekiwanie na pojedynczy obiekt
DWORD WaitForSingleObject( HANDLE hHandle, DWORD
dwMilliseconds);
• Oczekiwanie na grupę obiektów
DWORD WaitForMultipleObjects( DWORD nCount,
CONST HANDLE *lpHandles,
BOOL bWaitAll, DWORD dwMilliseconds);
Muteksy
• Muteks jest obiektem, który posiada dwa
stany: sygnalizowany i niesygnalizowany. W
danej chwili właścicielem muteksu może być
tylko jeden obiekt.
Tworzenie Muteksów
HANDLE CreateMutex( LPSECURITY_ATTRIBUTES lpMutexAttributes,
BOOL bInitialOwner, LPCTSTR lpName );
• lpMutexAttributes – wskaźnik na strukturę SECURITY _
ATTRIBUTES określającej atrybuty zabezpieczeń i dostępu
do muteksu
• bInitialOwner – wartość TRUE powoduje, że właścicielem
utworzonego muteksu jest wątek, w którym go utworzono,
• lpName – nazwa muteksu, podanie wartości NULL spowoduje
utworzenie muteksu nienazwanego.
Muteksy
• Zwolnienie Muteksu
BOOL ReleaseMutex( HANDLE hEvent );
• Pobranie uchwytu muteksu nazwanego
HANDLE OpenMutex( DWORD dwDesiredAccess,
BOOL bInheritHandle, LPCTSTR lpName );
Zdarzenia
• Zdarzenia są obiektami dwustanowymi:
Od muteksów zdarzenia odróżnia
możliwość regulacji sposobu przejścia
zdarzenia w stan niesygnalizowany.
• Rodzaje odwołania:
– Ręczne odwołanie funkcją ResetEvent()
– Automatyczne odwołane po przepuszczeniu
jednego oczekującego wątku
Tworzenie Zdarzenia
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES
lpEventAttributes, BOOL bManualReset, BOOL
bInitialState, LPCTSTR lpName );
•lpEventAttributes – wskaźnik na strukturę
SECURITY _ ATTRIBUTES
Tworzenie Zdarzenia
• bManualReset – określenie sposobu przechodzenia
zdarzenia w stan niesygnalizowany; wartość TRUE
ręczne; wartość FALSE automatyczne
• bInitialState – określenie czy po utworzeniu zdarzenie
ma być w stanie sygnalizowanym (TRUE) czy też
niesygnalizowanym (FALSE),
• lpName – nazwa zdarzenia, podanie wartości NULL
spowoduje utworzenie zdarzenia nienazwanego
Zdarzenia
• Ręczne odwołanie zdarzenia
BOOL ResetEvent( HANDLE hEvent );
• Ustawienie zdarzenia
BOOL SetEvent( HANDLE hEvent );
PulseEvent
• Powoduje przejście zdarzenia w stan
sygnalizowany i natychmiastowy jego powrót w
stan niesygnalizowany. Reakcja wątków
oczekujących na zdarzenie zależy od
wybranego sposobu przejścia zdarzenia w stan
niesygnalizowany. Jeżeli jest to wykonywane
ręcznie wywołanie PulseEvent przepuszcza
wszystkie oczekujące wątki. W przeciwnym
wypadku z puli wątków oczekujących na
zasygnalizowanie zdarzenia wybierany jest tylko
jeden
Semafory
• Przechowuje liczbę całkowitą dzieki której
kontroluje ilość wątków wykonujących
daną część programu. Każdy wątek
wchodzący do sekcji zmniejsza wartość
licznika o 1. Jeżeli wartość licznika
osiągnie 0 więcej wątków nie zostanie
wpuszczonych do czasu opuszczenia
przez sekcji przez inny wątek.
Tworzenie Semafora
HANDLE CreateSemaphore(
LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
LONG lInitialCount, LONG lMaximumCount, LPCTSTR
lpName
);
• lpSemaphoreAttributes – wskaźnik na
strukturę SECURITY _ ATTRIBUTES
Tworzenie Semafora
• lInitialCount – początkowa wartość licznika
semafora,
• MaximumCount – maksymalna wielkość
licznika semafora
• lpName – nazwa semafora, podanie
wartości NULL spowoduje utworzenie
semafora nienazwanego.
Zwolnienie Semafora
BOOL ReleaseSemaphore(
HANDLE hSemaphore,
LONG lReleaseCount,
LPLONG lpPreviousCount );
lReleaseCount – wartość odjęta od semafora
lpPreviousCount – wskaźnik do zmiennej do zapisania
poprzedniej wartości semafora
Zegary Oczekujące
• Zegar oczekujący przechodzi w stan
sygnalizowany po upływie zadanego
okresu czasu lub w określonych
odstępach czasu z automatycznym
powrotem do stanu niesygnalizowanego.
Zegary umożliwiają np. regularne
wywoływanie określonych wątków
Zegar Oczekujący
HANDLE WINAPI CreateWaitableTimer(
LPSECURITY_ATTRIBUTES lpTimerAttributes, BOOL
bManualReset, LPCTSTR lpTimerName )
• lpTimerAttributes – wskaźnik na strukturę
SECURITY _ ATTRIBUTES
Zegar Oczekujący
• bManualReset – wartość TRUE oznacza, że
zegar jest ustawiany ręcznie; wartość FALSE
oznacza, że zegar jest przestawiany w stan
sygnalizowany automatycznie w określonych
odstępach czasu z automatycznym powrotem do
stanu niesygnalizowanego po wyjściu z funkcji
oczekującej,
• lpTimerName – nazwa zegara, podanie wartości
NULL spowoduje utworzenie zegara
nienazwanego.
Zegar Oczekujący
BOOL WINAPI SetWaitableTimer( HANDLE
hTimer, const LARGE_INTEGER *pDueTime,
LONG lPeriod, PTIMERAPCROUTINE
pfnCompletionRoutine, LPVOID
lpArgToCompletionRoutine, BOOL fResume );
• pDueTime – czas, po którym zegar
przejdzie w stan sygnalizowany;
Zegar Oczekujący
• lPeriod – okres czasu (w milisekundach)
po upływie którego zegar przechodzi w
stan sygnalizowany; jeżeli podana
wartość jest większa od zera, to zegar jest
automatycznie uruchamiany co określony
czas dopóki nie zostanie zatrzymany
poprzez wywołanie funkcji
CancelWaitableTimer lub ponownie
aktywowany przy użyciu funkcji
SetWaitableTime