1. díl - Úvod do vícevláknových aplikací v C# .NET

C# .NET Paralelní programování Úvod do vícevláknových aplikací v C# .NET

Vítejte u prvního dílu seriálu o programování vícevláknových aplikací neboli multithreadingu v C# .NET. 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. Dostaneme se i k nejnovějším technologiím z .NET frameworku jako jsou paralelní programování, úlohy nebo klíčová slova async a await.

Úvod do teorie vláken

Než začneme používat vlákna, popišme si jakým způsobem spouští aplikace náš operační systém. Jelikož jsme u C#, tak můžeme hovořit o Windows, avšak princip platí pro většinu ostatních. Windows je víceúlohový operační systém (multitasking). To znamená, že dokáže spustit více aplikací najednou. Přitom jádro procesoru dokáže obvykle spustit jen jednu instrukci v jeden čas. Jistě víte, že Windows dokázal 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 kolik aplikací chcete a to bez ohledu na to, kolikajádrový procesor vlastníte. Jak je to možné?

Windows mezi spuštěnými aplikacemi jednoduše rychle přepíná (přesněji to dělá tzv. scheduler a o přepínání hovoříme jako o timeslicingu). 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, Windows stále přepíná mezi aplikacemi, ovšem dokáže vykonávat několik instrukcí najednou na každém procesorovém jádru.

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. 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. Většinou mají aplikace však jen jeden proces.

Správce úloh Windows
Vlákno

V procesu může běžet několik jeho vláken. Každá aplikace má minimálně jeden proces, ve kterém běží její hlavní vlákno, případně nějaká další vlákna. Zatím, co hlavní vlákno máme připravené, další vlákna si vytváříme my sami. Operační systém spustí naše vlákno na nějakém jádru a potom ho rychle uspává a probouzí 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ší.

Synchronizace

Vše 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žíváme jiným vláknem, může v ní být nesmysl. Proměnné se také v jádrech 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 seriálu 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 z Microsoftu snaží co mohou, aby práci s nimi co nejvíce zjednodušili (a že se jim to daří), stále technologie ve výsledku aplikaci zkomplikuje. Určitě je používejte s rozvahou.

První vícevláknová aplikace

Naprogramujeme si první vícevláknovou aplikaci. Pro zjednodušení budeme nějakou dobu pracovat pouze v konzoli. Založte si nový projekt, který pojmenujte Prepinac. K projektu přidáme stejnojmennou třídu s následujícím obsahem:

class Prepinac
{

        public void Vypisuj0()
        {
                while (true)
                {
                        Console.Write("0");
                }
        }

        public void Vypisuj1()
        {
                while (true)
                {
                        Console.Write("1");
                }
        }

        public void Prepinej()
        {
                Thread vlakno = new Thread(Vypisuj0);
                vlakno.Start();
                Vypisuj1();
        }

}

První 2 metody třídy jsou velmi jednoduché a simulují nějakou delší činnost. První metoda do nekonečna vypisuje nuly do konzole, druhá metoda stejným způsobem vypisuje jedničky.

Metoda Prepinej() je pro nás již zajímavější. Sama spustí výpis jedniček, ale ještě předtím vytvoří nové vlákno, kterému přiřadí metodu pro výpis nul. Toto vlákno poté také spustí.

Vlákna jsou v .NETu reprezentována třídou Thread. V konstruktoru ji předáme delegát ThreadStart, který má následující podobu:

public delegate void ThreadStart();

Vláknu tedy můžeme předat bezparametrickou metodu typu void. V metodě main vytvoříme instanci přepínače a necháme ho přepínat:

static void Main(string[] args)
{
        Prepinac prepinac = new Prepinac();
        prepinac.Prepinej();
}

Když aplikaci spustíme, získáme takovýto výstup:

Přepínání vláken v C# .NET

Všimněte si, že jedničky a nuly nejsou úplně na střídačku, jak bychom mohli očekávat. Je vidět, jak vlákno chvíli běží a poté je uspáno. Intervaly se také liší, i když průměrně běží obě vlákna stejně dlouho.

Vlastnosti třídy Thread

Zatím pro nás budou důležité pouze tyto vlastnosti na třídě Thread:

  • IsAlive - Označuje, zda metoda vlákna běží.
  • Name - Každé vlákno si můžeme pojmenovat, což nám usnadní ladění aplikace

Hlavní vlákno aplikace

K hlavnímu vláknu aplikace se dostaneme pomocí statické vlastnosti Thread.Curren­tThread. Bohužel nemá ve výchozím stavu přiřazeno žádné jméno, zkusme mu ho přiřadit a vypsat.

static void Main(string[] args)
{
        Thread.CurrentThread.Name = "Hlavní vlákno";
        Console.WriteLine(Thread.CurrentThread.Name);
}

V příštím dílu se podíváme na uspávání vláken, jejich spojování a základy synchronizace.


 

  Aktivity (1)

Článek pro vás napsal David Čápka
Avatar
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.

Jak se ti líbí článek?
Celkem (14 hlasů) :
4.714284.714284.714284.714284.71428


 



 

 

Komentáře

Avatar
pracansky
Člen
Avatar
pracansky:

Zkoušel jsem to a nuly a jedničky jsou skutečně ve skupinkách. Tvoje vysvětlení by bylo logické na jednojádrovém procesoru kde se vlákna musí přepínat. Já mám ale 4 jádrový procesor. Po spuštění jedou 2 jádra naplno což naznačuje že každé vlákno jede na jednom jádře a jedou tedy v pravém smysly slova současně. Potom bych tedy čekal že to bude výpis vypadat spíš nějak takhle 0101101100101­0110101. Napadá mě že ten popisovaný efekt je spíš nějaký vedlejší efekt vlastnost výpisu do konzole.

Máš nějaké vysvětlení na tuhle šťouravou poznámku? ;)

 
Odpovědět  +1 29.4.2015 23:29
Avatar
Adam Ježek
Tým ITnetwork
Avatar
Odpovídá na pracansky
Adam Ježek:

To že máš 4 jádrový procesor neznamená, že se vlákna nepřepínají. To by fungovalo pouze pokud by všechny ostatní programy a OS potřebovaly pouze 2 jádra na procesoru a ty zbylé 2 by byly jenom pro tebe. I na 4jádrových procesorech se vlákna přepínají, jenom né tak často jako na jednojádrovém.

Odpovědět 12.12.2015 11:49
Programátor dělá co může. Počítač co chce. | Pokud mi dáš mínus, tak prosim, napiš proč!
Avatar
Luboš Běhounek (Satik):

Dokonce i jednovláknová aplikace přeskakuje mezi jádry :)

Odpovědět 12.12.2015 15:46
:)
Avatar
Ondřej Krsička
Redaktor
Avatar
Ondřej Krsička:

Davidovi se tam ty jedničky a nuly přepínají docela rychle. Mě se vypíšou čtyři řádky jedniček a pak až jdou nuly. Nevíte, čím je to ovlivněno? :) Mám dvoujádro.

Editováno 21. dubna 19:21
 
Odpovědět 21. dubna 19:20
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 4 zpráv z 4.