Umíme to s Delphi, 51. díl – vlákna a paralelní programování: úvod

Stejně tak jako člověk zvládá více činností zároveň, i počítač dokáže pracovat paralelně na více frontách. A pokud to nedokáže, umí alespoň vyvolat dojem, že je to pro něj hračka. Dnes se začneme zabývat tím, jak v Delphi přinutit aplikaci, aby prováděla více činností „zároveň“ – otevřeme problematiku paralelního programování a vláken.
Víceúlohové zpracování – multitasking

Než si ukážeme, jak programovat paralelně (tedy jak zajistit běh více činností zároveň), podíváme se stručně na historii konceptu, který s paralelním během úzce souvisí: jedná se o tzv. multitasking. Později si ukážeme, jak multitasking souvisí s vlákny. Multitasking asi všichni velmi dobře známe a často využíváme – je to možnost spustit více aplikací zároveň. Multitasking tedy dále přináší možnost přepínat mezi spuštěnými aplikacemi (např. známá klávesová zkratka Alt-Tab). V dnešní době jej považujeme za samozřejmost a bez hlavního panelu, který spuštěné aplikace zobrazuje, si Windows ani neumíme představit. Bylo tomu tak ale vždy?

Než se na historickou procházku vydáme, je nutné zdůraznit jeden fakt, na který se občas zapomíná. Mezi uživateli-začátečníky je všeobecně rozšířena představa, že při použití vláken (a vůbec při paralelním programování) lze přinutit počítač, aby skutečně, fyzicky dělal více věcí najednou. Přesněji, že je možné, aby procesor zároveň prováděl více operací. Jde o omyl: procesor počítače dokáže v jednom okamžiku provádět nejvýše jednu jedinou operaci.

Poznámka pro pokročilé uživatele:

Předchozí věta je sice pravdivá, ale trochu zjednodušená: u RISCového procesoru - např. DEC Alpha - je v okamžiku provádění jedné operace druhá většinou značně "rozpracována" stejně jako třetí, čtvrtá a pátá (tzv. pipelining).

Jakákoliv forma paralelního běhu je na jednoprocesorovém počítači tedy pouhou iluzí, která je způsobena rychlým přepínáním běžících úloh. Spuštěné úkoly se střídají způsobem „každý chvilku tahá pilku“, a protože toto střídání je velmi rychlé, systém (jako celek) budí dojem, že běží všechny aplikace zároveň. Hovoříme-li tedy o multitaskingu, hovoříme o rychlém střídání běžících úloh. Všechny dosavadní formy multitaskingu pracovaly a budou pracovat na tomto principu! Jediný rozdíl tedy spočívá ve způsobu, jakým je toto střídání realizováno (zda si jej aplikace řídí samostatně nebo zda se o něj stará systém). Později si to popíšeme podrobněji. Jedinou možností, jak výpočet paralelizovat skutečně, je použití víceprocesorového počítače (víceprocesorové architektury) – samozřejmě za příslušné podpory operačního systému. Tento fakt je nutné mít stále na paměti, jinak vaše představy o vláknech mohou být od počátku zkreslené.

V dalším textu tohoto dílu (i dílů příštích) budeme pod pojmem „paralelní zpracování“ mít na mysli vždy toto „pseudoparalelní zpracování“, tedy velmi rychlé přepínání běžících úloh. Budeme brát v potaz jednoprocesorový (tedy „obyčejný“) počítač a budeme se snažit přinutit jej, aby co možná nejvěrohodněji budil zdání paralelního běhu více úloh zároveň. Víceprocesorovými stroji se zabývat nebudeme.

A nyní již pojďme na slíbenou exkurzi do historie. Začněme v dobách dávno minulých – v dobách starého dobrého MS-DOSu. Upřímně řečeno, zastávka v tomto období bude velmi krátká: MS-DOS víceúlohové zpracování nepodporoval vůbec. V tomto systému nebylo možné spustit více aplikací zároveň, nebylo ani možné běžící aplikaci nějak „sebrat“ řízení a odstranit ji z operační paměti. Operační systém fungoval pouze tak, že při požadavku na spuštění aplikace předal této aplikaci řízení a zůstal pasivním až do okamžiku, než mu aplikace toto řízení navrátila zpět. Jediná možná forma interakce spočívala v tzv. službách systému, které si mohla aplikace vyžádat (například vstupně/výstupní operace, apod.). O nějaké formě multitaskingu tedy můžeme jen těžko hovořit.

S příchodem 16bitových Windows 3.x se situace trochu zlepšila. Vylepšení ovšem nemohla být rázu technologického, ale spíše jen formálního, protože Windows 3.x nebyl kompletní, samostatný, ucelený, autonomní operační systém – byla to v podstatě nadstavba MS-DOSu. Přesto ale přinesl první možnost multitaskingu – tzv. kooperativní multitasking. V čem spočíval? Už z názvu vyplývá, že aby mohl být realizován, byla nutná jistá míra kooperace (spolupráce) aplikací. Aplikace po svém spuštění běžela až do okamžiku, kdy sama předala řízení někomu jinému. Aplikace měla vyhrazenu dobu na zpracování svého úkolu a po uplynutí této doby musela sama řízení předat. Pokud to neučinila, nikdo ji k tomu nemohl přinutit – neexistoval žádný mechanismus na systémové úrovni, který by neposlušnou (nebo zatuhlou) aplikaci odstranil z paměti a předal řízení někomu jinému. Po zatuhnutí jedné aplikace tak obvykle následoval pád celého systému.

Je zřejmé, že tento princip (už ze své definice) nemohl být příliš stabilní. Řešením proto měl být tzv. preemptivní multitasking, který se objevil s nástupem Windows 95. Protože Windows 95 už byly skoro autonomním 32-bitovým operačním systémem, který byl pro paralelní zpracování přece jen lépe připraven, nemusely už aplikace samy předávat řízení. O střídání běžících úloh se stará operační systém, který podle stanovených podmínek přiděluje strojový čas (výpočetní prostředky) mezi aplikacemi. Každá spuštěná aplikace dostane přidělen časový úsek, který smí využít ke svému výpočtu. Po vypršení tohoto času je aplikaci systémem poslána zpráva, která pozastaví provádění aplikace. Vzápětí se spustí další, čekající aplikace. Z této filozofie plyne celá řada výhod: pokud některá aplikace přestane reagovat, nic se nestane (teoreticky), neboť systém předá „štafetu“ jiné aplikaci a zbytky zatuhlého úkolu může odstranit z paměti. Systémy Windows 9x tak začaly být podstatně stabilnější než systémy Windows 3.x.

Poznámka pro skutečnosti znalé uživatele:

Asi všichni víme, že se stabilitou Windows to nebylo nikterak slavné ani po nástupu Windows 95. Není to ovšem způsobeno preemptivním multitaskingem. Důvod spočíval spíše v tom, že všechny aplikace používaly určitou část tzv. sdílené paměti, takže (zjednodušeně) libovolná aplikace, pokud se jí zachtělo, mohla přepsat data patřící někomu jinému. Možnost sebrat aplikaci procesor, kterou měl operační systém, tak zřejmě není dostatečným represivním opatřením proti neposlušným či jinak zhůvěřilým aplikacím…:)

Jak souvisí multitasking s vlákny?

Tolik tedy k multitaskingu. Možná se ale teď zamýšlíte nad tím, co má multitasking společného s vlákny. Multitasking je možnost „současného“ běhu více aplikací (přičemž víme, proč jsou u slova současného uvedeny uvozovky, takže od nynějška je tam již uvádět nebudu a vždy si je tam pouze přimyslíme). Někdy nám to ale nestačí. Představme si následující situaci.

Máme textový editor a program pro kompresi souborů. Pokud by neexistoval multitasking, museli bychom aplikace spustit postupně: nejprve bychom psali dopis, přičemž procesor by byl relativně nevyužit, zatímco my bychom se zapotili. Pak bychom komprimovali rozsáhlý adresář, přičemž procesor by nestíhal a my bychom se nudili. Protože existuje multitasking, spustíme obě aplikace najednou a zatímco píšeme dopis, procesor komprimuje adresář. Úspora času je zřejmá.

Jenže co by se stalo, kdyby obě činnosti (uznávám, není to příliš pravděpodobné:)) prováděla jedna a tatáž aplikace? Jak bychom mohli docílit podobné úspory času? Systém by nám v tomto případě nepomohl: předal by aplikaci procesor a pokud by zrovna potřebovala komprimovat, vůbec by nám neumožnila psát dopis. Jak zařídit, abychom mohli uvnitř jedné aplikace sami přepínat mezi několika běžícími úkoly?

Pojďme si řešení společně postupně objevit. Nejprve vytvoříme ukázkovou aplikaci, kterou budeme používat pro demonstrace dalších činností.

Normální, NEparalelní aplikace

Aplikace bude velmi jednoduchá a těžiště její činnosti bude spočívat ve vykreslování čar o náhodné poloze, délce a barvě. Tato činnost je poměrně pomalá a dobře nám poslouží pro ukázku řady vlastností.

Vytvořte novou aplikaci. Na formulář (frmHlavni) umístěte následující komponenty (v závorce je jejich vlastnost Name):

  • 3x Button (btnStart, btnStop, btnKonec)
  • Image (imgClassic)
  • Label (lblCas)

Nyní ošetřete důležité události, viz zdrojový kód a poznámky uvedené pod ním:

unit Hlavni;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

type
  TfrmHlavni = class(TForm)
    btnStart: TButton;
    btnStop: TButton;
    btnKonec: TButton;
    imgClassic: TImage;
    lblCas: TLabel;
    procedure btnStartClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure btnKonecClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  frmHlavni: TfrmHlavni;

implementation

{$R *.DFM}

procedure TfrmHlavni.btnStartClick(Sender: TObject);
var
  I: Integer;
  SourX1, SourX2, SourY1, SourY2: Integer;
  Barva: TColor;
  StartCas, StopCas: TTime;

begin
  btnStart.Enabled := False;
  btnStop.Enabled := True;

  StartCas := TTime(Now);

  for I := 1 to 10000 do begin
    SourX1 := Random(imgClassic.Width);
    SourY1 := Random(imgClassic.Height);
    SourX2 := Random(imgClassic.Width);
    SourY2 := Random(imgClassic.Height);

    Barva := TColor(Random($FFFFFF));
    imgClassic.Repaint;

    imgClassic.Canvas.Pen.Color := Barva;
    imgClassic.Canvas.MoveTo(SourX1, SourY1);
    imgClassic.Canvas.LineTo(SourX2, SourY2);
  end; // for I

  StopCas := TTime(Now);
  lblCas.Caption := FormatDateTime(`"Čas potřebný pro operaci: "nn:ss.zzz`,
    StopCas - StartCas);

  btnStart.Enabled := True;
  btnStop.Enabled := False;
end;

procedure TfrmHlavni.FormCreate(Sender: TObject);
begin
  frmHlavni.Caption := `Ukázka práce s vlákny`;
  btnStart.Caption := `&Start`;
  btnStop.Caption := `S&top`;
  btnKonec.Caption := `&Konec`;
  lblCas.Caption := ``;

  btnStop.Enabled := False;
end;

procedure TfrmHlavni.btnKonecClick(Sender: TObject);
begin
  Application.Terminate;
end;

end.

Poznámky k vytvořené aplikaci:

  • Nastavení podstatných vlastností je (jako vždy) z důvodu názornosti provedeno v obsluze události OnCreate hlavního formuláře.
  • Po klepnutí na tlačítko Start se nejprve zakáže tlačítko Start a povolí tlačítko Stop (zbytečně, protože v tomto příkladu stejně nebude tlačítko Stop použito :-)). Pak se zapamatuje aktuální systémový čas pro pozdější výpočet doby operace. Nakonec se spustí smyčka, která vykreslí do komponenty imgClassic 10000 čar náhodné pozice, délky a barvy. Místo čísla 10000 by správně měla být použita konstanta, použití čtyř proměnných (X1, Y1, X2, Y2) je zbytečné – mohla by vždy být přímo volána funkce Random, proměnné jsou použity především z důvodu přehlednosti.
  • Barva je určena jako náhodné číslo z intervalu 0 (černá) až FFFFFF hexadecimálně, což označuje maximální množství každé z barevných složek (červená, zelená, modrá) - tedy bílou. Získaná hodnota je přetypována na TColor, což je typ určený k uchovávání hodnoty barvy a navíc je to také typ vlastnosti Canvas.Pen.Color
  • Po vykreslení čáry je zavolána metoda imgClassic.Repaint. Zkuste si volání této metody zakomentovat – čáry nebudou v průběhu výpočtu vykreslovány a zobrazí se až po skončení výpočtu najednou. Akce bude několikrát rychlejší.
  • Na úplný závěr se vypíše čas potřebný pro celou akci. Použijeme k tomu funkci FormatDateTime, pomocí které vypíšeme čas ve formátu minuty:sekundy.milisekundy. Zbývá jen povolit tlačítko Start a zakázat Stop.
  • Je vidět, že v takto navržené aplikaci neexistuje žádná možnost, jak probíhající akci přerušit. Aplikace totiž v průběhu výpočtu vůbec nereaguje na žádné události a uživatelské vstupy – zkuste si třeba pohnout formulářem, případně jej uzavřít. Aplikace se zkrátka jeví jako zatuhlá. Je ovšem možné přepnout se do jiné aplikace – operační systém normálně reaguje (umí dělat „více věcí najednou“), jen aplikace nereaguje (dělá najednou jen jednu činnost).
Je vidět, že aplikace nedokáže zároveň vykreslovat čáry a provádět třeba obsluhy nějakých jiných událostí (např. klepnutí na tlačítko Konec, apod.). Aplikace je tedy kompletně neparalelní.

Zdánlivě paralelní aplikace poprvé: Application.ProcessMessages

Pokud sledujete náš seriál pravidelně, jistě vás už napadl první způsob, jak aplikaci trochu „zparalelnit“ – tedy jak umožnit, aby ji jedna probíhající činnost totálně nevytížila. Samozřejmě, jde o metodu Application.ProcessMessages. Upravte proto kód obsluhy události OnClick tlačítka btnStart takto:

procedure TfrmHlavni.btnStartClick(Sender: TObject);
var
  I: Integer;
  SourX1, SourX2, SourY1, SourY2: Integer;
  Barva: TColor;
  StartCas, StopCas: TTime;

begin
  btnStart.Enabled := False;
  btnStop.Enabled := True;

  StartCas := TTime(Now);

  for I := 1 to 10000 do begin
    SourX1 := Random(imgClassic.Width);
    SourY1 := Random(imgClassic.Height);
    SourX2 := Random(imgClassic.Width);
    SourY2 := Random(imgClassic.Height);

    Barva := TColor(Random($FFFFFF));

    imgClassic.Canvas.Pen.Color := Barva;
    imgClassic.Canvas.MoveTo(SourX1, SourY1);
    imgClassic.Canvas.LineTo(SourX2, SourY2);

    Application.ProcessMessages;
  end; // for I

  StopCas := TTime(Now);
  lblCas.Caption := FormatDateTime(`"Čas potřebný pro operaci: "
    nn:ss.zzz`, StopCas - StartCas);

  btnStart.Enabled := True;
  btnStop.Enabled := False;
end;

Všimněte si několika zajímavých věcí:

  • Volání Application.ProcessMessages provádění akce znatelně zpomalí.
  • Pokud se použije volání Application.ProcessMessages, není již nutné volat imgClassic.Repaint. MEtoda ProcessMessages provede překreslení, je-li třeba, sama. Naopak, pokud zařadíte i volání Repaint, celá operace bude trvat již neúnosně dlouhou. Tato poznámka ale platí pouze a jenom tehdy, když bude Repaint zařazeno PŘED vlastním kreslením, což nemá logiku (i když v předchozím příkladu to tak bylo:)). Pokud ho zařadím ZA kreslení (za metodu LineTo), potom už ProcessMessages NEPROVEDE další Repaint, protože prostě nemá proč.
  • Případné zařazení podmínky, která zavolá ProcessMessages např. jen při každém stém průběhu cyklem, dokáže provádění aplikace poměrně významně zrychlit.
  • Zařazení podmínky před ProcessMessages je ovšem trochu rozporuplné, protože podmínka je nejpomalejší operace v počítači. To, že podmínka pomohla zrychlit proces, není problém pomalosti ProcessMessages, ale toho, že po vykreslení musí následovat Repaint, pokud je před, pak musí ProcessMessages provést další RePaint a to je chyba programátora týkající se RePaint a ne ProcessMessages!
  • Celý problém spočívá tedy vlastně v tom, jestli se zavolá a nebo nezavolá RePaint. Jestli se zavolá, pak je teoreticky jedno jestli přímo nebo pomocí ProcessMessages, takže vše vlastně můžeme převést na problém: chcete vidět celé vykreslování nebo po 1000 čarách a nebo vůbec?

Celý pokus můžeme na vysvětlenou shrnout asi takto :

Víme, že: RePaint je pomalý => pokud ho zavoláme (my nebo systém), zpomalí se aplikace

ProcessMessages ProcessMessages
nezavolám zavoláme
RePaint nezavoláme aplikace je vytížena a provede RePaint až po skončení cyklu => aplikace je rychle hotova, ale nevidíme nic kreslit díky ProcessMessages se v každém cyklu provede RePaint => ve srovnání s RePaint je vlastní režie ProcessMessages zanedbatelná, a tak je výsledek stejný jako při volání RePaint bez ProcessMessages, ale je možné aplikaci do určité míry ovládat
RePaint zavoláme uvidíme kreslit čáry, ale aplikace bude neúnosně pomalá a nepůjde v době kreslení ovládat pokud zavoláme RePaint po nakreslení každé čáry a potom bude následovat ProcessMessages, nebude se muset okno v ProcessMessages překreslovat a lze tak režii ProcessMessages zanedbat => výsledek stejný jako při volání RePaint bez ProcessMessages, ale je možné aplikaci do určité míry ovládat

Tak trochu paralelní aplikace podruhé: událost OnIdle

Dalším způsobem, jak aplikaci přinutit, aby alespoň částečně simulovala paralelní zpracování, je použití události OnIdle objektu Application. Tato událost je generována vždy, když je aplikace nečinná, „v klidu“. Pokud bychom tedy do obsluhy této události naprogramovali jednotlivé kroky výpočtu (tedy generování a vykreslování úseček), měli bychom zajištěno, že se úsečky budou vykreslovat jen v okamžiku, kdy „na to bude čas“. Nemůže tak dojít ke zdánlivému zatuhnutí aplikace. Museli bychom ovšem trochu pozměnit strukturu našeho zdrojového kódu (především bychom v obsluze této události museli zjistit, zda má vykreslování zrovna probíhat nebo ne), proto berte tento odstavec jen jako poznámku, že je možné se tímto směrem ubírat.

Opravdu paralelní řešení: procesy a vlákna

Dosud uvedené možnosti „paralelního“ :-) programování (které jsou k dispozici již od Windows 3.x) znamenají sice krok správným směrem, nicméně v žádném případě nejsou zcela optimálním řešením. Mezi jejich největší nevýhody patří jistá nemožnost zasahovat do toho, co a kdy se má provádět (ne že by to u vláken dokonale šlo, nicméně určité prostředky k upřednostnění zvolených akcí máme).

S příchodem Windows 95 (a tedy preemptivního multitaskingu) dostali vývojáři Windows aplikací do ruky ohromně silný nástroj: tzv. procesy a vlákna. To jsou skutečné, pravé nástroje paralelního programování a s jejich pomocí se realizuje drtivá většina dnešních Win32 aplikací. Představte si třeba, že kopírujete v Průzkumníku rozsáhlý adresář. Na obrazovce vidíte okénko, ve kterém přelétává papírek z jednoho místa na druhé. Vykreslování papírku ale není jediná činnost, kterou v tom okamžiku Průzkumník provádí: probíhá totiž ještě vlastní kopírování. Průzkumník k implementaci tohoto jevu používá vláken: jedno vlákno kopíruje soubory a druhé vykresluje animaci (zjednodušený popis).

To je příjemné, nicméně stále ještě nevíme, co to vlastně vlákna jsou. Pojďme si to podrobně vysvětlit. Díky vláknům dokáží aplikace dělat více činností najednou (naposledy pro jistotu zopakujme, že ve skutečnosti dokáží pouze budit dojem, že dělají více činností najednou). Operační systém neustále (velmi rychle) přepíná mezi jednotlivými procesy a vlákny: střídavě „uspává“ běžící úlohu a budí ten „spící“ úkol, který je na řadě. Ponechme zatím stranou pravidla, kterými se řídí systém při určování „který úkol je právě na řadě“.

Důležité je, že systém nemusí nutně přepínat jen mezi aplikacemi (jako celky), ale i mezi jednotlivými vlákny (tedy mezi jednotlivými akcemi, které aplikace provádějí). Dejme tomu, že vaše aplikace provádí čtyři činnosti implementované do čtyř vláken: systém nepředává prostředky aplikaci jako celku, ale postupně jejím jednotlivým vláknům. Vlákna tedy umožní „rozsekat“ aplikaci na několik „podúloh“, které se budou ve svém běhu rychle střídat a vzbudí tedy dojem, že běží všechny zároveň. Z toho plyne důležitý důsledek: ani jako vývojáři aplikace nemáme žádnou možnost, jak exaktně určit, které vlákno naší aplikace má zrovna běžet. Vše je plně v kompetenci operačního systému a my to musíme pouze mít na paměti. Nelze tedy třeba počítat s tím, že „vlákno A děla kratší činnost, a proto skončí dříve než vlákno B“, protože systém nemusí systémový čas přidělovat rovnoměrně. Později si ukážeme, že existují postupy, jak běh vláken synchronizovat (tedy například počkat s nějakou akcí až do skončení zvoleného vlákna, apod.). Řekli jsme si, že vlákna ve skutečnosti neběží paralelně, my si ovšem při programování musíme představovat, že paralelně běží! Pokud si budeme říkat cosi ve smyslu „paralelně to stejně neběží, takže nehrozí, že by se tady něco změnilo mezitím než tohle skončí“, jsme na cestě do pekel.

Pozor: někteří začátečníci mají poněkud zkreslenou představu o tom, co znamená „přepínání mezi aplikacemi“. Myslí si, že když v hlavním pruhu systému Windows (nebo třeba pomocí Alt-Tab) vyberou některou běžící aplikaci, systém Windows provede „přepnutí“ právě na tuto aplikaci a pozastaví běh ostatních až do okamžiku, než některou z nich zase uživatel takto otevře. To je omyl! V zásadě platí, že systém přepíná mezi všemi běžícími aplikacemi, bez ohledu na to, jsou-li právě aktivní či maximalizované. Uvedenou klávesovou zkratkou pouze aplikaci zobrazíme, vidíme ji na ploše, ale ostatní (skryté, minimalizované, neaktivní) aplikace dále běží a systém jim nadále přiděluje strojový čas!

Vlákna versus procesy

V předchozích odstavcích jsme používali pojmy vlákno a proces. Nyní je vhodná doba, abychom si vysvětlili, co tyto pojmy znamenají – nebo spíše jaký je mezi nimi rozdíl. K tomu si ale musíme říci několik slov k architektuře dvaatřicetibitových Windows.

Proces je aplikace nebo program, který systém Windows zavedl do operační paměti („spustil“). Systém Windows pro každou spuštěnou aplikaci (tedy pro každý proces) vytváří tzv. virtuální adresní prostor, tedy paměťový prostor, ve kterém se aplikace v paměti nachází.

Vlákno je naproti tomu objektem systému Win32, ve kterém běží programový kód. Je to tedy objekt, který skutečně provádí nějakou operaci; je to objekt, ve kterém probíhá strojový kód vzniklý překladem zdrojového kódu některým překladačem (např. překladačem Borland Delphi). Vlákno je skutečnou pracovní jednotkou.

A nyní přijde důležitý bod: při každém vytvoření nového procesu (tedy při každém spuštění aplikace) se vytvoří vlákno, ze kterého se aplikace opravdu spustí. Toto vlákno se vytvoří zcela automaticky, postará se o to operační systém a vývojář pro to nemusí učinit zhola nic. Z toho plyne důležitý poznatek: všechny aplikace, které jsme až dosud v našem seriálu programovali, běží v nějakém vláknu – aniž jsme o tom tedy věděli, vláken jsme již využívali.

Takže shrňme: po spuštění aplikace vzniká proces a jeho adresní prostor. Tento proces způsobí vytvoření vlákna, z něhož pak běží fyzický programový kód příslušné aplikace. Takto vytvořené vlákno se nazývá primární vlákno. Toto vlákno může ovšem vytvořit další vlákna – a to je již úkol pro nás. Takže: pokud někde uvnitř kódu aplikace napíšeme příkaz pro vytvoření vlákna (jak si to brzy ukážeme), dojde vlastně k tomu, že primární vlákno naší aplikace vytvoří další vlákno. Naše aplikace (proces) se tedy v tomto okamžiku skládá ze dvou pracovních vláken. Je zřejmé, že nemusíme vytvářet jen jedno další vlákno, ale libovolné množství; stejně tak každé z námi vytvořených vláken může vytvořit další vlákno (vlákna).

Důležité ovšem je, že všechna vlákna vytvořená v rámci jednoho procesu (v rámci jedné aplikace) využívají tentýž, společný virtuální adresní prostor tohoto procesu! Adresní prostor je definován procesem, nikoliv vláknem – proto je možné sdílet data procesu více vlákny. To je samozřejmě velmi pozitivní, nicméně – jak už to tak v podobných případech bývá – přináší to i celou řadu úskalí a nebezpečí (později si je ukážeme).

Povězme si v rámci úvodního seznámení s vlákny poslední maličkost, související s počtem vláken provádějících tentýž programový kód. Vzhledem k tomu, že více vláken může sdílet tatáž data (jednoho procesu), je samozřejmě možné, aby více vláken provádělo tentýž programový kód. Je tedy možné vytvořit třeba pět vláken, která budou počítat faktoriál čísla – přitom všechna vlákna jej budou počítat podle stejného předpisu, podle stejného programového kódu. A nebo je možné v každém samostatném vlákně provádět jeden, samostatný blok programového kódu (vytvoříme tedy pět vláken, přičemž každé bude dělat něco jiného). Vícevláknové zpracování se nazývá multithreading.

Zopakujme tedy nejdůležitější informace, které byste si měli z tohoto úvodu odnést. Spuštěním aplikace vzniká proces a tento proces způsobí vytvoření primárního vlákna aplikace. Toto primární vlákno ve dvaatřicetibitových Windows provádí vlastní činnost spuštěné aplikace. Kromě toho je možné (ručně) vytvářet další vlákna. Skutečnou práci, činnost, vlastní programový kód provádějí vlákna, nikoliv procesy. Proces definuje adresní prostor a data, vlákna mohou tyto prostředky využívat a sdílet.

Zdá se vám to složité? Nemějte obavy, při praktickém programování tyto detaily nebudete příliš potřebovat. Za týden, až vytvoříme svou první aplikaci využívající vlákna, uvidíte, že situace je mnohem jednodušší, než se zdá z tohoto teoretického pohledu.

Jasně a jednou provždy: co je vlákno?

Je možné, že pořád ještě postrádáte jasnou odpověď na otázku, co je to vlastně vlákno. Už si jej (snad) umíte představit, jaksi cítíte, oč jde, ale jasná definice nemůže uškodit. Tady je:

Vlákno je samostatná výpočetní aktivita v rámci jedné aplikace (tedy sdílí data, otevřené soubory a další zdroje aplikace).

Za týden

Dnes jsme si prozradili nezbytný teoretický základ k vláknům. Řekli jsme si, co je multitasking a jak se vyvíjel, definovali a popsali jsme si vlákna jako taková. Za týden společně vytvoříme první aplikaci používající vlákna – a to nebude všechno:)
Diskuze (4) Další článek: Výkonní Outsideři: Verto GeForce 4 MX 440 / 420

Témata článku: , , , , , , , , , , , , , , , , , , , , , , , , ,