Programové prostředky pro multithreading - Java, POSIX, WinAPI, OpenMP. 

V programovacím jazyce Java jsou objekty a metody potřebné pro práci s vlákny dostupné ve standardních knihovnách.

Základem je třída Thread

tvoří základ všech paralelních programů v Javě

pro jednoduché programy stačí oddědit od této třídy a překrýt metodu run(), do které se napíše výkonný kód vlákna.

protože někdy je potřeba, aby naše třída dědila od jiné a zároveň měla vlastnosti vlákna, existuje ještě rozhraní Runnable, které stačí implementovat a třída rovněž získá vlastnosti vlákna

pro napsání paralelního programu stačí vytvořit potřebné třídy, napsat jejich výkonné kódy do metod run(), a pak vlákna spustit (vlákna se spouští metodou start()

Stop - po jejím volání vlákno uvolní všechny držené monitory --> může zanechat uvolněné objekty v nekonzistentním stavu

V javě:

Monitory v Javě

komunikace vláken je řešena přes sdílenou paměť

kritické sekce jsou ošetřeny klíčovým slovem synchronized

monitor je součástí každého objektu

pro implementaci monitorů jsou uvnitř objektu monitoru skryté atributy:

1 zámek – pro všechny synchronizované metody

1 fronta – pro blokovaná vlákna čekající na vstup do synchronizované metody (kritická sekce)

kromě těchto implicitních monitorů objektu lze v metodě ještě vytvořit synchronizační blok, který může použít i jiný zámek, než je implicitní zámek objektu, což dovoluje jemnější členění vzájemného vyloučení (např. jen na část metody)

Struktury v posixu jsou reprezentovány pomocí handle. Struktury jsou pthread_t, pthreat_mutex_t a pthread_cond_t. Objekty mají sadu atributů, které lze měnit pthread_attr_t, pthread_mutexattr_t, pthread_condattr_t. Funkce pro manipulaci s objekty buď vracejou 0 jako OK, nebo jinou hodnotu jako chybový kód.

Program vlákna je vlastně funkce C s typem void* prog_name(void* arg)

vlákno se vytvoří následujícím způsobem:

status = pthread_create(&worker, NULL, prog_name,…);

vlákno běží hned po vytvoření

vlákno se ruší pomocí pthread_exit (ze stejného vlákna) nebo pthread_cancel (z cizího)

způsob plánování: FIFO – nejprioritnější kategorie (nejdříve se plánují vlákna tétokategorie), RR – round-robin, FG – foreground, implicitní kategorie, střídání vláken, ty s vyšší prioritou mají více času, BG – background, všechna vlákna se střídají, ale dostávají méně času než FG

rozměr zásobníku

hlídač zásobníku – jak daleko se můžeme od konce zásobníku dostat, jinak vznikne výjimka

konec vlákna - vlákno dojde na konec svého programu - na toto ukončení se lze synchronizovat z jiného vlákna pomocí pthread_join(na_koho_se_čeká, kam_přijde_výsledek) nebo zabito z vnějšku – pokud možno nepoužívat, základní funkce pro likvidaci pthread_cancel(oběť); oběť se může bránit pthread_setcancelstate(…), k likvidaci nemůže dojít kdekoliv v kódu vlákna, ale jen v předem připravených místech (volání blokující funkce, volání pthread_testcancel()), popisovaný způsob je synchronní, existuje i asynchronní)

Je to synchronizační primitivum, umožňuje implementaci vzájemného vyloučení v kritické sekci. Jen jedno vlákno vlastnící mutex může být v kritické sekci, ostatní vlákna čekají. 

Podmínková proměnná

Vlákno se uspí do té doby, dokud se nesplní nějaká podmínka, která se pak signalizuje pomocí podmínkové proměnné. Podmínková proměnná je svázána s mutexem.

Pthread_cond_wait - blokující operace, vlákno se převede do stavu waiting, a odemkne se zámek

Má virtuální paměťový prostor, systémové zdroje, bezpečnostní kontext (kdokoliv nemůže provádět cokoliv), jedinečný identifikátor, spustitelný kód

Prioritní třída – vlákna pak mohou mít prioritu pouze v rozsahu prioritní třídy procesu.

Má alespoň jedno vlákno (primární - to hlavní), které může vytvářet další vlákna – threads a fibers

Entita v rámci procesu, kterou plánuje OS. Všechny vlákna sdílí paměťový prostor a zdroje svého procesu. Vlastní handler výjimek. Priorita: OS zajišťuje inverze priorit, dynamic boost, foreground/input včetně realtime.

Jedinečný identifikátor

TLS – thread local storage, data, která jsou specifická/lokální pro daný thread. Možnost, jak si thread může zabezpečit jedinečný přístup ke svým datům. Každý proces má k dispozici několik TLS slotů, které mohou být použity jeho thready. Možnost využití ke zpracování výjimek.

Běží v kontextu vlákna, plánuje ho thread procesu, jeden thread může naplánovat několik fibers. FLS – fiber local storage =analogie k TLS, FLS je asociováno s threadem.

Má malý kontext (v porovnání s kontextem threadu) – zásobník, podmnožinu registrů a inicializační data. 

Má přístup do TLS threadu, v jehož kontextu běží.

Synchonizační objekty WinApi

Mutex – vyžaduje přepnutí do režimu jádra, CreateMutex, OpenMutex, ReleaseMutex

Event – stavy signaled/nonsignaled, múže být buď pulsní, tj, překlopí se do nonsignaled jakmile propustí jednoho čekajícího, nebo zůstane signaled (Manual reset Event, Auto Reset Event). CreateEvent, SetEvent, ResetEvent.

Pozn. txkoutny říkal, že event je rychlejší kvůli tomu, že nevyžaduje přepnutí do režimu jádra - lhal. Mutex, Event i Semaphore vyžadují přepnutí, protože jsou implementovány na úrovni jádra a umožňují synchronizaci napříč různými procesy (mohou být pojmenované). Opět v přednáškách je jaksi uvedeno, že kritická sekce má velkou režii - není to pravda, kritická sekce jako jediná neumožňuje synchronizaci mezi procesy, ale pouze v rámci jednoho procesu. Díky tomu při běžném vstupu/výstupu z/do ní (pokud je volná) není nutné nikam přepínat. Do režimu jádra se přepne pouze v případě, že je kritická sekce obsazená a je nutné čekat na uvolnění - pak se přepne do režimu jádra a čeká se nad nějakým synchronizačním primitivem, např. EVENT nebo MUTEX.

Semafor – klasický semafor, CreateSemaphore, OpenSemaphore, ReleaseSemaphore

Kritická sekce – Critical section, má velkou režii, EnterCriticalSection (blokující), TryEnterCriticalSection (neblokující), LeaveCriticalSection

Na signalizaci objektů lze čekat pomocí WaitForSingleObject nebo WaitForMultipleObjects.

Knihovny pro tvorbu paralelních aplikací bez nutnosti tvoření vláken v kódu:

Idea je taková, že vezmu sériový program a jen s minimem změn a s pomocí direktiv preprocesoru určím, které úseky kódu se budou vykonávat paralelně. Pokud se překladač vyvolá s direktivou OpenMP přeloží se jako paralelní program, jinak jako sériový. Pomocí direktiv pak lze překladači říci, které proměnné jsou sdílené a vyžadují tedy synchronizaci (kritickou sekci) a které nikoli.
