IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 1 - Úvod do vícevláknových aplikací v C a C++

Vítejte u první lekce kurzu o programování vícevláknových aplikací neboli multithreadingu v C++ a C. Naučíme se zde plně využívat moderní vícejádrové procesory a také spouštět na pozadí úlohy, které by jinak zasekly hlavní vlákno aplikace.

Úvod do teorie vláken

Než začneme používat vlákna, popišme si jakým způsobem spouští naše aplikace operační systém. Všechny moderní operační systémy (Windows, Linux i MacOS) jsou víceúlohové. To znamená, že dokáží spustit více aplikací (úloh) najednou. To ale neplatí pro procesor samotný. Zjednodušeně můžeme říci, že jedno jádro procesoru dokáže spustit jen jednu instrukci v jeden čas. Na druhou stranu někteří z vás vědí, že většina operačních systémů dokázala spouštět více aplikací najednou dlouho předtím, než byly na trhu vícejádrové procesory a i teď si můžete spustit neomezené množství aplikací (nebo minimálně rozumné množství), a to bez ohledu na to, kolikajádrový procesor vlastníte. Jak je to možné?

Vícevláknové aplikace v C++ - Paralelní programování a vícevláknové aplikace v C++

Operační systém mezi spuštěnými aplikacemi jednoduše rychle přepíná (přesněji to dělá tzv. plánovač, anglicky scheduler a o přepínání hovoříme jako o změně contextu, anglicky context switching). Systém jednu aplikaci zastaví a spustí na okamžik aplikaci jinou. Uživatel to vnímá jako by aplikace běžely najednou, i když tomu tak ve skutečnosti není. S příchodem vícejádrových procesorů se princip nezměnil, operační systém stále přepíná mezi aplikacemi, ale může jich nechat běžet více současně (v závislosti na počtu jader).

Aplikace, proces a vlákno

Zamysleme se nad termíny aplikace, proces a vlákno. Aplikaci si jistě dokážeme představit, je to např. internetový prohlížeč, ve kterém čtete tento článek. Co je ovšem proces a co vlákno?

Proces

Proces je instance běžící aplikace. Pokud si spustíme 3x kalkulačku, nalezneme ve správci úloh 3x proces calc.exe (ostatní platformy prominou). Některé složitější aplikace mohou využívat několik procesů, např. Google Chrome vytvoří proces pro každou otevřenou záložku. Operační systém mezi sebou jednotlivé procesy izoluje, to znamená, že procesy na sebe navzájem nevidí (například jeden proces nevidí do paměti jiného procesu). Komunikace mezi procesy se poté musí řešit skrz operační systém a jedná se o složitější problematiku, kterou se zde nebudeme zabývat. Nakonec jen dodám, že většina aplikací používá pouze jeden proces, vzhledem k tomu, že procesy mohou využívat více vláken.

Správce úloh Windows - Paralelní programování a vícevláknové aplikace v C++

Vlákno

V procesu může běžet několik vláken. Každá aplikace má minimálně jeden proces, ve kterém běží minimálně jedno vlákno (tzv. hlavní vlákno). Zatím, co hlavní vlákno máme připravené od operačního systému, další vlákna si vytváříme sami. Operační systém spustí naše vlákno na nějakém jádru procesoru a potom ho rychle uspává a probouzí (v důsledku přepínání) jak se mu to zrovna hodí. Ve výsledku nám vlákna v procesu běží paralelně a můžeme např. provádět nějaké složité analýzy, aniž bychom zasekli hlavní vlákno aplikace a to dokonce i v několika vláknech najednou, přičemž bude samotný výpočet několikrát rychlejší.

Na rozdíl od procesů, vlákna jsou "lehčí" jednotkou. Vlákna mezi sebou nejsou nijak izolovaná. To hlavně znamená, že je paměť mezi vlákny sdílená, čehož se hojně využívá. Má to ale i své nevýhody, musíme zajistit, aby byl přístup do paměti synchronizovaný, tj. aby například jedno vlákno nezapisovalo tam, odkud druhé vlákno zrovna čte. Další nevýhodou je, že chyba v jednom vlákně ukončí celý proces a tedy i všechna ostatní vlákna.

Synchronizace

Zvýšení výkonu, propustnosti, odezvy a dalších atributů aplikace zní skvěle, že? Vlákna se ovšem v praxi používají opravdu jen pokud je nutně potřebujeme. Je s nimi totiž spojený jeden obrovský problém a tím je synchronizace. Nikdy nevíme, kdy budou naše vlákna uspána, systém to provede bez ohledu na to, co dané vlákno právě dělá. Asi si dokážeme představit, že se metoda vlákna zasekne na nějakém řádku zdrojového kódu. Ve skutečnosti se však může zaseknout i např. v polovině sčítání, protože na 32bitovém procesoru jsou ke sčítání 64bitových čísel zapotřebí 2 instrukce. Pokud tuto hodnotu použijeme jiným vláknem, může v ní být nesmysl. Proměnné se také v jádrech procesoru cachují, takže se může stát, že má ve stejný čas stejná proměnná několik různých hodnot. K problémům synchronizace se dostaneme během kurzu poměrně podrobně a naučíme se je také řešit.

Vlákna určitě nejsou něco, co by každá aplikace musela nutně obsahovat a ačkoli se inženýři snaží co mohou, aby práci s nimi co nejvíce zjednodušili (a že se jim to daří), stále ve výsledku aplikaci zkomplikují (a v horším případě i mohou aplikaci degradovat). Určitě je používejte s rozvahou a pouze pokud k tomu máte důvod.

Vlákna v C a C++

Situace s vlákny je v C a C++ poněkud komplikovaná. Je to dáno historickým vývojem a hlavně faktem, že C je hlavní jazyk pro programování operačního systému. Z historického hlediska bylo několik snah o zavedení tzv. multitasking systémů (tedy systémů schopných spustit několik úloh současně). K tomu byly použity různé technologie (namátkou vyjmenujme například nepreemptivní plánování nebo vlákna implementovaná v uživatelském prostoru, kdy operační systém o vláknech vůbec nevěděl).

Tato a další rozhraní se v některých případech dochovala až do dnešních dnů. Například v linuxovém světě se můžete stále setkat s označením lightweight process, který není nic jiného než označení vlákna (ang. thread), které se dochovalo z dřívějška. Z tohoto (a milionu dalších) důvodů je rozhraní pro práci s vlákny roztříštěné. Konkrétně se v kurzu podíváme na následující rozhraní:

  • Knihovna threads.h, která je určena pro C a je přidána ve standardu C11. Každý kompiler, který podporuje C11, by měl být schopen tuto knihovnu použít bez ohledu na operační systém, pro který programujeme.
  • Knihovna thread. Toto je knihovna, která je určena pro C++, protože programátoři v C++ chtějí pracovat s objekty. Byla přidána ve standardu C++11 a měla by nabídnout stejnou funkcionalitu na všech platformách.
  • Knihovna pthread neboli POSIX threads. Jedná se opět o knihovnu pro C a je de facto standardní knihovnou pro práci s vlákny pro UNIX-like operační systémy (tedy Linux a MacOS).
  • Windows Threading Library je určena pro jazyk C a operační systém Windows.

Zmíněné knihovny se mezi sebou liší více, než bychom si my jako programátoři přáli. Co se vytváření a práce se vlákny týče, knihovny jsou téměř identické (koneckonců, vytváření a mazání vlákna je potřeba všude). Rozdíl nastává v synchronizačních entitách (tj. v objektech, které řídí synchronizaci mezi vlákny). Různé knihovny poskytují různé prostředky, které mohou v jiných knihovnách chybět.

Primárně se budeme zabývat knihovnou thread pro C++. Po probrání základů se podíváme na ostatní knihovny a řekneme si, co u nich chybí nebo co naopak nabízejí navíc. Jak v dalších lekcích uvidíme, tyto rozdíly nejsou zas tak drastické, jak by se na první pohled mohlo zdát. Poznáme totiž, že nám stačí pouze jeden synchronizační mechanismus (který poskytují všechny knihovny) k tomu, abychom dokázali přepsat všechny ostatní. Použití jiné knihovny nám poté může ušetřit nějaký čas, ale rozhodně kvůli tomu nebudeme muset přepsat celý projekt.

Kurz ukončíme několika praktickými příklady, kde si ukážeme nejčastější vícevláknové návrhové vzory, vysvětlíme si jak fungují a použijeme je v nějaké zjednodušené aplikaci.

V příští lekci, První vícevláknová aplikace v C++, se podíváme na vytváření vláken na různých platformách.


 

Všechny články v sekci
Paralelní programování a vícevláknové aplikace v C++
Přeskočit článek
(nedoporučujeme)
První vícevláknová aplikace v C++
Článek pro vás napsal Patrik Valkovič
Avatar
Uživatelské hodnocení:
13 hlasů
Věnuji se programování v C++ a C#. Kromě toho také programuji v PHP (Nette) a JavaScriptu (NodeJS).
Aktivity