Vlákna v C# - 1. díl
 x   TIP: Přetáhni ikonu na hlavní panel pro připnutí webu
Reklama
Reklama

Vlákna v C# - 1. dílVlákna v C# - 1. díl

 

Vlákna v C# - 1. díl

Google       Google       1. 7. 2008       50 927×

Plynule navážeme na minulý díl, ukážeme si, jak předávat data vláknům, zjistíme, že je můžeme pojmenovávat, a naučíme se ošetřovat vzniklé výjimky.

Reklama
Reklama

Vytváření a startování vláken

Vlákna jsou vytvářena pomocí konstruktoru třídy Thread předávajícího delegáta ThreadStart – ten označuje metodu, kde by měla začít práce vlákna. Takhle vypadá deklarace delegáta ThreadStart:

public delegate void ThreadStart();

Poté následuje zavolání metody Start() na instanci vlákna, tato akce uvede vlákno do provozu. Funguje až do chvíle, kdy zpracuje všechny příkazy, které jsme mu zadali. Když vše dokončí, Garbage Collector ho odklidí a uvolní paměť.

class Vlakno
{
    static void Main()
    {
        Thread t = new Thread(new ThreadStart(Pis));
        t.Start();    // Spustí Pis() na novém vlákně
        Pis();        // Zároveň s tím zavolá Pis() i na hlavním vlákně
    }

    static void Pis()
    {
        Console.WriteLine("Ahoj!");
    }
}

Tento kód vrátí jako výsledek dvě Ahoj!.

Nebylo by to C#, kdyby se nám celou věc nepokusilo trochu zjednodušit. Můžeme celé ThreadStart vypustit, kompilátor si ho tam umí doplnit sám:

static void Main() 
{
    Thread t = new Thread (Pis);
    ...
}

static void Pis(){ ... }

Další způsob, jak si ušetřit práci, je použití anonymních metod:

static void Main()
{
    Thread t = new Thread(delegate() { Console.WriteLine("Ahoj!"); });
    t.Start();
    Pis();
}

Vlákna mají vlastnost IsAlive, která vrací true, pokud bylo vlákno už spuštěno (tedy byla zavolána metoda Start()), až do jeho zániku. Vlákno po skončení své činnosti nemůže být restartováno, protože ho, jak už jsem zmínil, Garbage Collector odklidí.

Předávání dat delegátovi ThreadStart

Řekněme, že chcete v příkladu nahoře lépe rozlišit, které „Ahoj!“ napsalo které vlákno, třeba tím, že jedno ze slov napíšeme velkými písmeny. Normálně by šlo by předat nějaký parametr metodě Pis(), ale to nemůžeme, protože delegát ThreadStart nepřijímá žádné argumenty. Naštěstí má .NET framework další verzi delegáta a tou je ParametrizedThreadStart, který přímá argument typu object, tak jako v příkladu:

public delegate void ParameterizedThreadStart (object obj);

Upravený příklad z předchozí kapitoly bude vypadat takto:

class ThreadTest
{
    static void Main()
    {
        Thread t = new Thread(Pis);
        t.Start(true);             // == Pis(true) 
        Pis(false);
    }

    static void Pis(object velkaPismena)
    {
        bool velka = (bool)velkaPismena;
        Console.WriteLine(velka ? "AHOJ!" : "ahoj!");
    }
}

Divíte se, kde je nějaký delegát? Kompilátor si ho opět sám dosadí, pokud totiž předáte parametr volané metodě, automaticky se použije ParametrizedThreadStart namísto ThreadStart.

Parametr předávávaný delegátovi ParametrizedThreadStart přijímá právě jeden parametr typu object, při použití ho tedy vždy musíme přetypovat, stejně jako já to udělal s přetypováním na boolean.

Další možností, jak vyřešit příklad nahoře, je opět použití anonymních metod.

static void Main() 
{
    Thread t = new Thread(delegate() { Pis("Ahoj"); });
    t.Start ();
}
    
static void Pis (string text) 
{
    Console.WriteLine(text);
}

Výhoda tohoto postupu spočívá v tom, že metoda WriteLine přijímá libovolný počet argumentů a nejsme omezování jako při použití ParametrizedThreadStart.)

Do třetice, další způsob předávání dat je přes instanční metody namísto statických metod. Jednotlivé vlastnosti instance pak říkají vláknu, co má dělat.

class Vlakna
{
    bool velka;

    static void Main()
    {
        Vlakna instance1 = new Vlakna();
        instance1.velka = true;
        Thread t = new Thread(instance1.Pis);
        t.Start();
        Vlakna instance2 = new Vlakna();
        instance2.Pis();
    }

    void Pis()
    {
        Console.WriteLine(velká ? "AHOJ!" : "ahoj!");
    }
}

Pojmenovávání vláken

Vlákno můžeme pojmenovat přes vlastnost Name. Velmi to usnadňuje debugging (prostě víme, co je které vlákno zač) a s názvy vláken si můžeme hrát i v konzoli.

Jméno vlákna můžeme nastavit kdykoliv se nám zachce, jen musí existovat. Ale pozor, jméno můžeme nastavit jen jednou, jinak dostaneme výjimku!

V následujícím příkladu, protože neběží ve chvíli, kdy upravujeme název vlákna, více než jedno (hlavní) vlákno, můžeme k němu přistoupit přes statickou vlastnost CurrentThread.

class Pojmenovavani
{
    static void Main()
    {
        Thread.CurrentThread.Name = "hlavní";
        Thread pracovni = new Thread(Pis);
        pracovni.Name = "pracovní";
        pracovni.Start();
        Pis();
        Console.ReadKey();
    }

    static void Pis()
    {
        Console.WriteLine("Zdraví vás " + Thread.CurrentThread.Name + " vlákno");
    }
}

Vlákna běžící v popředí a pozadí

Ve výchozím stavu běží vlákna na popředí, to znamená, že aplikace funguje tak dlouho, dokud alespoň jedno z nich běží. C# umožňuje využití i vláken běžících na pozadí – pokud vypneme všechna vlákna na popředí, aplikace se vypne, i když nějaká vlákna v pozadí ještě fungují.

Změna vlákna z popředí na pozadí nezmění jeho prioritu vůči ostatním vláknům, ani potřebný procesorový čas.

Vlákna mají vlastnost IsBackground, která, jak jistě tušíte, nastavuje (pokud má hodnotu true) vlákno na vlákno běžící v pozadí.

class VlaknaNaPozadi
{
    static void Main(string[] args)
    {
        Thread pracovniV = new Thread(delegate() { Console.ReadLine(); });
        if (args.Length > 0) pracovniV.IsBackground = true;
        pracovniV.Start();
    }
}

Pokud je program spuštěn bez parametrů, pracovní vlákno je ve výchozím stavu – běží na popředí, a zastaví se na Console.ReadLine(), kde čeká, až uživatel stiskne klávesu Enter. Mezitím hlavní vlákno pořád běží a aplikace funguje, protože hlavní vlákno je aktivní.

Pokud bychom ale metodě Main() předali nějaký parametr, pracovní vlákno by se přepnulo do práce na pozadí a aplikace by se téměř okamžitě ukončila, protože hlavní (které běží v popředí) hned ukončí svoji práci a nebere ohledy na to, že nějaké vlákno na pozadí ještě běží.

Když je vlákno běžící na pozadí ukončeno takhle „násilně“, přeskočí se v něm i všechny případné bloky „finally“. Toto chování je nežádoucí (proč bychom nějaké finally vůbec psali, kdybychom ho chtěli přeskakovat), a proto je dobré navyknout si počkat vždy než vlákna na pozadí ukončí svou práci a do té doby práci vláken v popředí pozastavit, třeba pomocí Thread.Join (viz minulý díl).

Nastavovat pracovní vlákna jako vlákna běžící na pozadí je výhodné v tom, že máme snadnou kontrolu nad vypínáním aplikace. Představme si opak – vlákno v popředí, které samo při vypnutí aplikace (tedy vypnutí hlavního vlákna) nezemře. Taková aplikace sice zmizí ve Správci úloh ze záložky Aplikace, ale pořád bude její proces aktivní na záložce Procesy. Dokud sám uživatel neukončí na záložce Procesy daný proces, bude běžet a spotřebovávat systémové zdroje.

Nejčastějším zdrojem problémů vypínaných aplikací jsou zapomenutá vlákna běžící na popředí!

Priorita vláken

Vlastnost vláken zvaná Priority určuje, kolik dané vlákno dostane času na vykonání své činnosti. Vzpomínáte na minulý díl, kde jsem se zmiňoval, že CLR přepíná mezi vlákny každou přibližně desetinu milisekundy? Právě vlastností Priority se dá tato hodnota mírně upravit.

Tato vlastnost je udělaná jako výčet (typ enum) hodnot Lowest, BelowNormal, Normal, AboveNormal a High (seřazeno od nejnižší priority po nejvyšší). Nastavená hodnota se projeví jen tehdy, pokud je zároveň spuštěno více vláken.

Ošetřování výjimek

Jakékoliv „obecné“ try/catch/finally bloky nemají žádný význam, pokud je nové vlákno spuštěné, běží totiž na jiné úrovni a bloky, jako v příkladu níže, bude ignorovat.

public static void Main()
{
    try
    {
        new Thread(Pis).Start();
    }
    catch (Exception ex)
    {
        // Sem se ani nikdy nedostaneme!   
        Console.WriteLine("Výjimka!");
    }
}

static void Pis() { throw null; } 

Ke bloku catch ani nedojde, takže ani try by tam nemuselo být. Výsledkem bude nová vlákno s neošetřenou výjimkou NullReferenceException. Řešením je napsání těchto bloků zvlášť pro každou metodu, kterou nové vlákno spouští.

public static void Main() 
{
    new Thread (Pis).Start();
}
 
static void Pis() 
{
    try 
    {
        ...
        throw null;      // tuhle výjimku to už zachytí
        ...
    }
    catch (Exception ex) 
    {
        //Nějaké ošetření výjimky…
        ...
    }
}

Od .NET frameworku 2.0 výše, jakákoliv neošetřená výjimka na vlákně shodí celou aplikaci, takže ignorovat je není způsob jak daný problém vyřešit. Bloky try/catch musí být v každé metodě (abychom 100% zamezili pádům), což při větším počtu metod začíná být skutečně nepraktické. Jste Windows Forms programátor a používáte časté „globální“ zachycování výjimek?

static class Program 
{
    static void Main() 
    {
        Application.ThreadException += Osetreni;
        Application.Run (new MainForm());
    }
 
    static void Osetreni (object sender, ThreadExceptionEventArgs e) 
    {
        // Zachycení, zapsaní, ošetření výjimky…
    }
}

Událost Application.ThreadException se zavolá, když naposled volaný kód (jako odpověď na nějakou Windows zprávu) vytvoří výjimku. Toto řešení sice funguje skvěle, ale dává nám falešný pocit bezpečí. Chyby vytvořené pracovními vlákny totiž ani ThreadException nezachytí. Naštěstí máme k dispozici low-level řešení – AppDomain.UnhandledException. K zavolání dojde kdykoliv, kdy dojde na jakémkoliv vlákně k chybě, v jakémkoliv typu aplikace (ať už s UI nebo bez něj). Ovšem nedoporučuji používat tuto událost jako primární pro zachycení výjimek, použijte ji spíš jako poslední záchranu před pádem aplikace.

A to je konec, příště nás čeká přehled způsobů jak synchronizovat práci vláken.

Zdroj: http://www.albahari.com/threading/#_Creating_Starting

×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.

Hlasování bylo ukončeno    
0 hlasů
Google
(fotka) Jakub KottnauerJakub studuje informatiku na FIT ČVUT, jeho oblíbenou platformou je .NET.
Web     Twitter     Facebook     LinkedIn    

Nové články

Obrázek ke článku Delphi 10.1.2 (Berlin Update 2) – na co se můžeme těšit

Delphi 10.1.2 (Berlin Update 2) – na co se můžeme těšit

Touto roční dobou, kdy je zem pokrytá barevným listím a prsty křehnou v mrazivých ránech, se obvykle těšíme na zbrusu novou verzi RAD Studia. Letos si však ale budeme muset počkat na Godzillu a Linux až do jara. Vezměme tedy za vděk alespoň updatem 2 a jelikož dle vyjádření pánů z Embarcadero se budou nové věci objevovat průběžně, pojďme se na to tedy podívat.

Reklama
Reklama
Obrázek ke článku Konference: Moderní datová centra pro byznys dneška se koná už 24. 11.

Konference: Moderní datová centra pro byznys dneška se koná už 24. 11.

Stále rostoucí zájem o cloudové služby i maximální důraz na pružnost, spolehlivost a bezpečnost IT vedou k výrazným inovacím v datových centrech. V infrastruktuře datových center hraje stále významnější roli software a stále častěji se lze setkat s hybridními přístupy k jejich budování i provozu.

Obrázek ke článku Konference: Mobilní technologie mají velký potenciál pro byznys

Konference: Mobilní technologie mají velký potenciál pro byznys

Firmy by se podle analytiků společnosti Gartner měly  rychle přizpůsobit skutečnosti, že mobilní technologie už zdaleka nejsou horkou novinkou, ale standardní součástí byznysu. I přesto - nebo možná právě proto - tu nabízejí velký potenciál. Kde tedy jsou ty největší příležitosti? I tomu se bude věnovat již čtvrtý ročník úspěšné konference Mobilní řešení pro business.

Obrázek ke článku Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres přiveze v září do Prahy špičky světové kryptoanarchie

Hackerský kongres HCPP16 pořádá od 30. září do 2. října nezisková organizace Paralelní Polis již potřetí, a to ve stejnojmenném bitcoinovém prostoru v pražských Holešovicích. Letos přiveze na třídenní konferenci přes 40 většinou zahraničních speakerů – lídrů z oblastí technologií, decentralizované ekonomiky, politických umění a aktivismu. Náměty jejich přednášek budou také hacking, kryptoměny, věda, svoboda nebo kryptoanarchie.

loadingtransparent (function() { var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true; po.src = 'https://apis.google.com/js/plusone.js'; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s); })();
Hostujeme u Českého hostingu       ISSN 1801-1586       ⇡ Nahoru Webtea.cz logo © 20032016 Programujte.com
Zasadilo a pěstuje Webtea.cz, šéfredaktor Lukáš Churý