Paralelní programování a .NET 4.0
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu

Paralelní programování a .NET 4.0Paralelní programování a .NET 4.0

 

Paralelní programování a .NET 4.0

Google       Google       28. 8. 2010       28 518×

Nová verze populárního frameworku .NET se v našich počítačích ještě ani pořádně neohřála, a tak je teď ta nejvhodnější chvíle představit si zajímavé novinky z oblasti paralelního programování a multithreadingu.

Na světě dnes již převládají multi-core počítače, počínaje těmi osazenými procesory Core 2 Duo a nejmodernějšími Intel i7 konče. Logickým krokem je pak využití všech dostupných jader, aby naše aplikace mohla zpracovávat více úloh najednou. Samozřejmě, podpora více vláken je v .NET Frameworku už pěknou řádku let, ale kvůli své složitosti to není příliš oblíbené téma. Ono se není ani čemu divit, je potřeba dávat si pozor na velké množství věcí, jen se podívejte na můj seriál o multithreadingu zde na Programujte.

Vývojáři .NET 4.0 se tak pokusili alespoň částečně vyřešit tento problém přidáním nových knihoven souhrnně zvaných „Parallel Extensions“. Tato rozšíření („extensions“) mají dvě části - Task Parallel Library (TPL) a PLINQ (Parallel LINQ). PLINQ ve skutečnosti vnitřně pro svou funkčnost používá TPL. TPL je složena z několika nových API převážně v namespace System.Threading.Tasks.

Na začátek je potřeba jedno malé vysvětlení – jaký je rozdíl mezi multithreadingem a paralelismem? Paralelní programování je ve skutečnosti multithreading (tedy používá vlákna), ale samotným pojmem multithreading se spíše myslí používání vláken na různé nesouvisející úkony (tedy že jedno vlákno se stará o uživatelské rozhraní, druhé počítá číslo pí atp.). Na druhou stranu paralelismus znamená, že jednu složitou akci rozdělíme na více vláken.

Pojďme se podívat na pár příkladů, jak Parallel Extensions využít. Začneme knihovnou TPL.

Něco snadného – paralelní cykly

Měli jste někdy kolekci s tisíci objektů, kterými jste museli procházet, a říkali jste si „jen kdyby to šlo snadno rozdělit na víc vláken“? Teď už si to říkat nemusíte, nemusíte vědět ani nic o multithreadingu, stačí jen použít trochu jinou verzi cyklu for nebo foreach. Kód řekne víc než tisíc slov (a nejdříve nezapomeňte vložit jmenný prostor System.Threading.Tasks):

// standardní sekvenční přístup
foreach (var i in list)
{
    Console.WriteLine(i);
}
            
// paralelní přístup
Parallel.ForEach(list, i => Console.WriteLine(i));

V zásadě to funguje poměrně snadno. TPL vezme kolekci, rozdělí ji na několik částí, vytvoří několik vláken a jednotlivá vlákna se pak starají o jednotlivé části kolekce. A to aniž byste vy, jakožto vývojáři, museli napsat jediný řádek pro vytvoření vlákna nebo něčeho podobného.

Metoda For() funguje obdobně:

// vypíše čísla {0,1,2}
Parallel.For(0,3, i => Console.WriteLine(i));

Vlákna jsou přeci jenom trochu složitější tématika, a tak se může stát, že se dostaneme do složitější situace, kdy bychom si chtěli fungování těchto dvou metod trochu upravit k obrazu svému. I na to bylo pomyšleno, a proto mají obě metody velké množství přetížených verzí, více o nich na MSDN.

O stupínek těžší – paralelizace úkolů

Task Parallel Library. To by mělo mít něco společného s úkoly, neříkáte si? A taky že má, „úkol“ stojí přímo ve středu funkčnosti této nové knihovny. Pod slovem „úkol“ si můžeme představit nějakou asynchronní akci, vlastně něco, pro co bychom v „obyčejném“ multithreadingu vytvořili nové vlákno.

Pro kohokoliv, kdo kdy zkusil práci s vlákny, musí být výhody hned jasné:

  • Automatická správa ThreadPoolu (zjednodušeně řečeno je to místo, odkud se berou vlákna), všem úkolům, které zařadíme do fronty je postupně přiřazeno vlákno. To vše je děláno s ohledem na největší efektivitu.
  • Lepší a pohodlnější kontrola, než je možná s klasickými vlákny – okolo úkolů je postaveno bohaté API, které poskytuje další úroveň abstrakce nad běžnými vlákny.

V základu máme dva způsoby, jak vykonávat úkoly – implicitně a explicitně.

Implicitní vykonávání úkolů

Pro implicitní vykonávání slouží metoda Parallel.Invoke(). Dává nám hezkou cestu, jak spouštět jednoduše několik metod najednou, stačí jen několik delegátů odkazujících na jednotlivé statické metody. Následující příklad ukazuje tři různé způsoby, jak toho docílit, a vězte, že jich je ještě víc. V praxi je ovšem ideální se držet jen jednoho, kvůli přehlednosti:

Parallel.Invoke(() => NejakaPrace(), 
    new Action(NejakaDalsiPrace),
    delegate() { Console.WriteLine("třetí způsob"); });

Pokud bychom chtěli trochu víc možností, musíme vykonávat úkoly explicitně.

Explicitní vykonávání úkolů

Úkol je reprezentován instancí třídy System.Threading.Tasks.Task. Pokud vrací po svém splnění nějakou hodnotu, použijeme odvozenou třídu Task. Obě třídy poskytují množství metod a vlastností, například si uveďme vlastnost Status, která nám řekne, jestli byl už úkol spuštěn, jestli skončil s chybou, jestli byl zrušen apod.

Úkol vytváříme pomocí delegátu, který obaluje kód úkolu:

var prvniUkol = new Task(() => Console.WriteLine("Zdraví vás první úkol!"));
prvniUkol.Start();

Console.WriteLine("Tady vás zdraví hlavní vlákno");
Console.WriteLine("Je už úkol dokončen? " + prvniUkol.IsCompleted); 

Když si tento kód párkrát spustíte, zjistíte, že bude na otázku „Je už úkol dokončen?“ střídavě odpovídat true/false. Můžete to brát jako důkaz toho, že je úkol skutečně spuštěn na jiném vlákně a jednou je rychlejší ono, podruhé zase to hlavní.

Úkoly můžeme vytvářet i spouštět v jednom kroku, jak ukazuje tento kód:

var prvniUkol = Task.Factory.StartNew(() => Console.WriteLine("Zdraví vás první úkol!"));

Vlastnost Factory použitá v kódu výše rozhodně stojí za pozornost. Tato vlastnost vrací instanci třídy TaskFactory a pomocí ní pak voláme metodu StartNew(). Není to ale jediná metoda, kterou máme k dispozici, zajímavá je například i ContinueWhenAll(), která přijímá sadu úkolů, po jejichž ukončení bude spuštěn jiný úkol.

Vraťme se teď trochu zpátky k odvozené třídě Task, která nám umožňuje vracet po ukončení úkolu data. Následuje příklad:

Task<double>[] ukoly = new Task<double>[]
{
    Task.Factory.StartNew(() => Pocitej1()),
    Task.Factory.StartNew(() => Pocitej2()),
    Task.Factory.StartNew(() => Pocitej3())                
};

double[] vysledky = new double[ukoly.Length];
            
for (int i = 0; i < ukoly.Length; i++)
    vysledky[i] = ukoly[i].Result;

Není to nic složitého: na začátku vytvoříme pole úkolů, do kterého rovnou tři umístíme a rovnou je spustíme. Pak si vytvoříme nové pole („vysledky“), do kterého budeme ukládat hodnoty vrácené úkoly. Každý úkol odvozený od třídy Task<TResult> poskytuje vlastnost Result, která po jeho skončení obsahuje výsledek, a toho využijeme na konci kódu. Metody Pocitej1, 2 a 3 jsou jen statické metody, které něco počítají a pak vrací hodnotu typu double.

To byla jen stručná ukázka možností knihovny Task Parallel Library. Uvedené příklady by vám měly pomoci s paralelizací alespoň těch nejjednodušších úloh. Teď přichází na řadu Parallel LINQ.

Parallel LINQ - použití

Pokud se dotazujete velkého množství dat pomocí LINQ, kdy už by se paralelizaci vyplatilo využít, je nasazení PLINQ většinou hračka.

PLINQ totiž podporuje úplně všechny operátory jako LINQ a stejně jako LINQ používá tzv. „deferred execution“ (překlad „opožděné volání“ je asi nejvýstižnější). Znamená to, že dotaz není zavolán hned při deklaraci, ale až při jeho použití dál v kódu (například ve chvíli, kdy se na data zeptáme pomocí cyklu foreach apod.).

Dobře, ale jak na to? Stačí zavolat metodu AsParallel() na cílovou kolekci uvnitř dotazu a je hotovo! Porovnejte následující dva kousky kódu, první pomocí klasického LINQ a druhý pomocí PLINQ:

List<int> list = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var query_linq = from i in list
            where i % 2 == 0
            select i;

var query_plinq = from i in list.AsParallel()
                    where i % 2 == 0
                    select i;

Toto byl ten nejjednodušší příklad použití, který by měl pro množství situací stačit. Pochopitelně máme k dispozici spoustu nejrůznějších nastavení a možností, například jak omezit počet použitých jader procesoru, jak ošetřovat výjimky, jak zastavovat volání dotazů a tak dále. Zmíněné věci by si zasloužily spíše svůj článek, proto si o nich povíme někdy příště. Doufám, že vám článek ukázal některé ze zajímavých novinek v .NET 4.0.

×Odeslání článku na tvůj Kindle

Zadej svůj Kindle e-mail a my ti pošleme článek na tvůj Kindle.
Musíš mít povolený příjem obsahu do svého Kindle z naší e-mailové adresy kindle@programujte.com.

E-mailová adresa (např. novak@kindle.com):

TIP: Pokud chceš dostávat naše články každé ráno do svého Kindle, koukni do sekce Články do Kindle.

1 názor  —  1 nový  
Hlasování bylo ukončeno    
0 hlasů
Google
Jakub studuje informatiku na FIT ČVUT, jeho oblíbenou platformou je .NET.
Web     Twitter     Facebook     LinkedIn    

Nové články

Obrázek ke článku Stavebnice umělé inteligence 1

Stavebnice umělé inteligence 1

Článek popisuje první část stavebnice umělé inteligence. Obsahuje lineární a plošnou optimalizaci.  Demo verzi je možné použít pro výuku i zájmovou činnost. Profesionální verze je určena pro vývojáře, kteří chtějí integrovat popsané moduly do svých systémů.

Obrázek ke článku Hybridní inteligentní systémy 2

Hybridní inteligentní systémy 2

V technické praxi využíváme často kombinaci různých disciplín umělé inteligence a klasických výpočtů. Takovým systémům říkáme hybridní systémy. V tomto článku se zmíním o určitém typu hybridního systému, který je užitečný ve velmi složitých výrobních procesech.

Obrázek ke článku Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Jak vést kvalitně tým v IT oboru: Naprogramujte si ty správné manažerské kvality

Vedení týmu v oboru informačních technologií se nijak zvlášť neliší od jiných oborů. Přesto však IT manažeři čelí výzvě v podobě velmi rychlého rozvoje a tím i rostoucími nároky na své lidi. Udržet pozornost, motivaci a efektivitu týmu vyžaduje opravdu pevné manažerské základy a zároveň otevřenost a flexibilitu pro stále nové výzvy.

Obrázek ke článku Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Síla týmů se na home office může vytrácet. Odborníci radí, jak z pracovních omezení vytěžit maximum

Za poslední rok se podoba práce zaměstnanců změnila k nepoznání. Především plošné zavedení home office, které mělo být zpočátku jen dočasným opatřením, je pro mnohé už více než rok každodenní realitou. Co ale dělat, když se při práci z domova ztrácí motivace, zaměstnanci přestávají komunikovat a dříve fungující tým se rozpadá na skupinu solitérů? Odborníci na personalistiku dali dohromady několik rad, jak udržet tým v chodu, i když pracovní podmínky nejsou ideální.

Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032024 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý